/*
 * Copyright (C) 2008,2009,2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#include "exl.h"
#include "ejit.h"

#if HAVE_EXECINFO_H
#  include <execinfo.h>
#endif

#define SIMPLE_JIT_DEBUG	0

/*
 * Types
 */
typedef eobject_t (*jit_t)(ethread_t *);
#define call_jit(jit, thread)		(*jit)(thread)

/*
 * Prototypes
 */
static void
emit_note(efunction_t *function);

static void
call_vv(void(*vv)(void));

static void
call_vi(void(*vi)(eint32_t), eint32_t a0);

static void
call_pi(eobject_t(*pi)(eint32_t), eint32_t a0);

static void
call_vii(void(*vii)(eint32_t, eint32_t), eint32_t a0, eint32_t a1);

static void
call_pii(eobject_t(*p_ii)(eint32_t, eint32_t), eint32_t a0, eint32_t a1);

static void
call_viii(void(*viii)(eint32_t, eint32_t, eint32_t),
	  eint32_t a0, eint32_t a1, eint32_t a2);

static void
call_viiii(void(*viiii)(eint32_t, eint32_t, eint32_t, eint32_t),
	    eint32_t a0, eint32_t a1, eint32_t a2, eint32_t a3);

static void
emit_enter(east_node_t *node);

static void
emit_vnewi(east_node_t *node);

static void
emit_stackref(eint32_t offset, eint32_t type);

static void
emit_try(east_list_t *list);

static void
record_forward_call(jit_node_t *call, eint32_t offset);

static jit_node_t *
emit_frame(void);

static void
emit_call(eint32_t offset);

static void
emit_method_call(eint32_t offset, eint32_t type);

static void
record_forward_method(jit_node_t *call, eword_t offset, eword_t type);

static void
emit_explicit_call(eint32_t offset, eint32_t type);

static eword_t
allocate_forward_label(elabel_t *label);

static void
record_forward_patch(jit_node_t *instr, eword_t label);

static void
emit_branch(elabel_t *label);

static ecode_t
switches(ehash_t *hash, eint32_t offset);

static void
record_table_patch(eword_t offset);

static void
emit_table_branch(ehash_t *hash, eint32_t offset);

static void
vswitch_forward(evswitch_t *table);

static void
hswitch_forward(ehswitch_t *table);

static void
table_forward(eobject_t object);

static void
vswitch_patch(evswitch_t *table);

static void
hswitch_patch(ehswitch_t *table);

static void
table_patch(eobject_t object);

static void
zero_sign_extend(eint32_t type);

static void
zero_sign_extend_reg(eint32_t type, jit_int32_t lreg, jit_int32_t hreg);

static void
load_ireg(jit_int32_t base, eint32_t offset);

static void
load_ireg_type(jit_int32_t base, eint32_t offset, eint32_t type);

static void
load_ireg_reg_type(jit_int32_t base, eint32_t offset,
		   jit_int32_t lreg, jit_int32_t hreg,
		   eint32_t type);

static void
load_ireg_implicit(void);

static void
load_ireg32_implicit(void);

static void
maybe_reload_ireg_implicit(void);

static void
maybe_reload_ireg32_implicit(void);

static void
maybe_reload_extend_ireg_implicit(eint32_t type);

static void
store_ireg(jit_int32_t base, eint32_t offset);

static void
store_ireg_value_type(jit_int32_t base, eint32_t offset);

static void
store_ireg_implicit(ebool_t type);

#define RGZ_signed		0x01
#define RGZ_ptr_to_v1		0x10
#define RGZ_r1_to_v1		0x20
#define RGZ_r1_pop_to_v1	0x30
#define RGZ_r2_to_v1		0x40
#if __WORDSIZE == 32
static void
store_ireg_implicit_rgz(eint32_t flag, jit_int32_t l, jit_int32_t h);
#else
static void
store_ireg_implicit_rgz(eint32_t flag, jit_int32_t r);
#endif

static void
store_int32(jit_int32_t base, eint32_t offset, eint32_t type);

static void
store_int(jit_int32_t base, eint32_t offset, eint32_t type);

static void
load_freg(jit_int32_t base, eint32_t offset);

static void
load_freg_reg_type(jit_int32_t base, eint32_t offset,
		   jit_int32_t dreg, jit_int32_t freg, eint32_t type);

static void
load_freg_type(jit_int32_t base, eint32_t offset, eint32_t type);

static void
maybe_reload_freg_implicit(void);

static void
load_freg_implicit(void);

static void
store_freg(jit_int32_t base, eint32_t offset);

static void
store_freg_value_type(jit_int32_t base, eint32_t offset);

static void
store_freg_implicit(ebool_t type);

static void
store_float32(jit_int32_t base, eint32_t offset, eint32_t type);

static void
store_float(jit_int32_t base, eint32_t offset, eint32_t type);

static void
store_dynamic(jit_int32_t base, eint32_t offset, eint32_t type);

static void
emit_clear(void);

static void
emit_flush(void);

static void
emit_sync(void);

static void
emit_sync_uint(void);

#define PROTO		1
#  include "eemit_code.c"
#undef PROTO

/*
 * Initialization
 */
jit_state_t		 *_jit;
eobject_t		  ethread_exit_code;
ehash_t			 *eswitch_hash;
static jit_node_t	 *thread_exit_label;
static jit_uint8_t	 *the_code;
static jit_uint8_t	 *the_end;
static eword_t		 *patches;
static jit_node_t	**labels;
static eword_t		 *tables;
static eword_t		 *calls;
static eword_t		 *methods;
static evector_t	 *root_vector;
static evector_t	 *patch_vector;
static evector_t	 *label_vector;
static evector_t	 *table_vector;
static evector_t	 *call_vector;
static evector_t	 *method_vector;

static eint32_t		  ejit_type;

/* implicit value in register
 * if set, the thread implicit value is in register based on type */
#define ejit_sync_register	(1 << 0)

/* implicit value type is not statically known
 * if ejit_sync_register is also set, implicit value is in gpr or fpr
 * if the dynamic type allows */
#define ejit_sync_dynamic	(1 << 1)

/* implicit value in thread state
 * if set (or ejit_sync_register not set) the thread implicit value
 * is in memory */
#define ejit_sync_implicit	(1 << 2)

/* implicit value in register have been zero or sign extended
 * used when the implicit value fits in a 32 bit register, and only
 * meaningul when need two integer registers to represent the implicit value */
#define ejit_sync_int_ext	(1 << 3)

/* implicit value has been converted to/from float/double
 * meaningful when loading/storing different float type values */
#define ejit_sync_float_ext	(1 << 4)

/* implicit int value has been converted to a float/double
 * it means the implicit value is an integer type, and the float
 * register has been converted, or vice-versa, that is, implicit is
 * a float type, but integer register has been truncated */
#define ejit_sync_int_float	(1 << 5)
#define ejit_sync_int_double	(1 << 6)
#define ejit_sync_long_float	(1 << 7)
#define ejit_sync_long_double	(1 << 8)
static eint32_t		  ejit_sync;

static char		 *exceptions[] = {
    "nothing",
    "out of memory",
    "floating point error",
    "out of bounds",
    "null dereference",
    "type mismatch",
    "input output error",
    "invalid argument",
    "not a number",
    "not an integer",
    "not a real number",
    "not a rational number",
    "not a 32 bits integer",
    "not a finite real number",
};

/*
 * Implementation
 */
void
init_emit(void)
{
#if SIMPLE_JIT_DEBUG
    jit_node_t		*done;
    jit_node_t		*node;
    eobject_t		*root;

    /* at least arm needs it set to > 1 to properly print constant buffers */
    if (cfg_verbose < 2)
	cfg_verbose = 2;

    init_jit();
#if 0
    jit_cpu.sse2 = 0;
#endif
    init_debug();

    evector((eobject_t *)&root_vector, t_void, 8, 1);
    eadd_root((eobject_t *)&root_vector);
    root = root_vector->ptr;

    jit_new_state(root + 0);
    _jit = root[0];

    jit_prolog();

#  if 0
    node = jit_label();
    jit_addr(JIT_R0, JIT_R0, JIT_R1);
    jit_addi(JIT_R0, JIT_R0, 1);
    jit_addcr(JIT_R0, JIT_R0, JIT_R1);
    jit_addxr(JIT_R0, JIT_R0, JIT_R1);
    jit_addci(JIT_R0, JIT_R0, 1);
    jit_addxi(JIT_R0, JIT_R0, 1);
    jit_subr(JIT_R0, JIT_R0, JIT_R1);
    jit_subi(JIT_R0, JIT_R0, 1);
    jit_subcr(JIT_R0, JIT_R0, JIT_R1);
    jit_subxr(JIT_R0, JIT_R0, JIT_R1);
    jit_subci(JIT_R0, JIT_R0, 1);
    jit_subxi(JIT_R0, JIT_R0, 1);
    jit_mulr(JIT_R0, JIT_R0, JIT_R1);
    jit_muli(JIT_R0, JIT_R0, 1);
    jit_divr(JIT_R0, JIT_R0, JIT_R1);
    jit_divi(JIT_R0, JIT_R0, 1);
    jit_divr_u(JIT_R0, JIT_R0, JIT_R1);
    jit_divi_u(JIT_R0, JIT_R0, 1);
    jit_remr(JIT_R0, JIT_R0, JIT_R1);
    jit_remi(JIT_R0, JIT_R0, 1);
    jit_remr_u(JIT_R0, JIT_R0, JIT_R1);
    jit_remi_u(JIT_R0, JIT_R0, 1);
    jit_andr(JIT_R0, JIT_R0, JIT_R1);
    jit_andi(JIT_R0, JIT_R0, 1);
    jit_orr(JIT_R0, JIT_R0, JIT_R1);
    jit_ori(JIT_R0, JIT_R0, 1);
    jit_xorr(JIT_R0, JIT_R0, JIT_R1);
    jit_xori(JIT_R0, JIT_R0, 1);
    jit_lshr(JIT_R0, JIT_R0, JIT_R1);
    jit_lshi(JIT_R0, JIT_R0, 1);
    jit_rshr(JIT_R0, JIT_R0, JIT_R1);
    jit_rshi(JIT_R0, JIT_R0, 1);
    jit_rshr_u(JIT_R0, JIT_R0, JIT_R1);
    jit_rshi_u(JIT_R0, JIT_R0, 1);
    jit_negr(JIT_R0, JIT_R1);
    jit_comr(JIT_R0, JIT_R1);
    jit_ltr(JIT_R0, JIT_R0, JIT_R1);
    jit_lti(JIT_R0, JIT_R0, 1);
    jit_ltr_u(JIT_R0, JIT_R0, JIT_R1);
    jit_lti_u(JIT_R0, JIT_R0, 1);
    jit_ler(JIT_R0, JIT_R0, JIT_R1);
    jit_lei(JIT_R0, JIT_R0, 1);
    jit_ler_u(JIT_R0, JIT_R0, JIT_R1);
    jit_lei_u(JIT_R0, JIT_R0, 1);
    jit_eqr(JIT_R0, JIT_R0, JIT_R1);
    jit_eqi(JIT_R0, JIT_R0, 1);
    jit_ger(JIT_R0, JIT_R0, JIT_R1);
    jit_gei(JIT_R0, JIT_R0, 1);
    jit_ger_u(JIT_R0, JIT_R0, JIT_R1);
    jit_gei_u(JIT_R0, JIT_R0, 1);
    jit_gtr(JIT_R0, JIT_R0, JIT_R1);
    jit_gti(JIT_R0, JIT_R0, 1);
    jit_gtr_u(JIT_R0, JIT_R0, JIT_R1);
    jit_gti_u(JIT_R0, JIT_R0, 1);
    jit_ner(JIT_R0, JIT_R0, JIT_R1);
    jit_nei(JIT_R0, JIT_R0, 1);
    jit_movr(JIT_R0, JIT_R1);
    jit_movi(JIT_R0, 1);
    jit_extr_c(JIT_R0, JIT_R1);
    jit_extr_uc(JIT_R0, JIT_R1);
    jit_extr_s(JIT_R0, JIT_R1);
    jit_extr_us(JIT_R0, JIT_R1);
    jit_truncr_f_i(JIT_R0, JIT_F0);
    jit_truncr_d_i(JIT_R0, JIT_F0);
    jit_htonr(JIT_R0, JIT_R1);
    jit_ldr_c(JIT_R0, JIT_R1);
    jit_ldi_c(JIT_R0, 0x80000000);
    jit_ldr_uc(JIT_R0, JIT_R1);
    jit_ldi_uc(JIT_R0, 0x80000000);
    jit_ldr_s(JIT_R0, JIT_R1);
    jit_ldi_s(JIT_R0, 0x80000000);
    jit_ldr_us(JIT_R0, JIT_R1);
    jit_ldi_us(JIT_R0, 0x80000000);
    jit_ldr_i(JIT_R0, JIT_R1);
    jit_ldi_i(JIT_R0, 0x80000000);
    jit_ldxr_c(JIT_R0, JIT_R1, JIT_R2);
    jit_ldxi_c(JIT_R0, JIT_R1, 4);
    jit_ldxr_uc(JIT_R0, JIT_R1, JIT_R2);
    jit_ldxi_uc(JIT_R0, JIT_R1, 4);
    jit_ldxr_s(JIT_R0, JIT_R1, JIT_R2);
    jit_ldxi_s(JIT_R0, JIT_R1, 4);
    jit_ldxr_us(JIT_R0, JIT_R1, JIT_R2);
    jit_ldxi_us(JIT_R0, JIT_R1, 4);
    jit_ldxr_i(JIT_R0, JIT_R1, JIT_R2);
    jit_ldxi_i(JIT_R0, JIT_R1, 4);
    jit_str_c(JIT_R1, JIT_R0);
    jit_sti_c(0x80000000, JIT_R0);
    jit_str_s(JIT_R1, JIT_R0);
    jit_sti_s(0x80000000, JIT_R0);
    jit_str_i(JIT_R1, JIT_R0);
    jit_sti_i(0x80000000, JIT_R0);
    jit_stxr_c(JIT_R2, JIT_R1, JIT_R0);
    jit_stxi_c(4, JIT_R1, JIT_R0);
    jit_stxr_s(JIT_R2, JIT_R1, JIT_R0);
    jit_stxi_s(4, JIT_R1, JIT_R0);
    jit_stxr_i(JIT_R2, JIT_R1, JIT_R0);
    jit_stxi_i(4, JIT_R1, JIT_R0);
    jit_patch_at(jit_bltr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_blti(JIT_R0, 1), node);
    jit_patch_at(jit_bltr_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_blti_u(JIT_R0, 1), node);
    jit_patch_at(jit_bler(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_blei(JIT_R0, 1), node);
    jit_patch_at(jit_bler_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_blei_u(JIT_R0, 1), node);
    jit_patch_at(jit_beqr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_beqi(JIT_R0, 1), node);
    jit_patch_at(jit_bger(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bgei(JIT_R0, 1), node);
    jit_patch_at(jit_bger_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bgei_u(JIT_R0, 1), node);
    jit_patch_at(jit_bgtr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bgti(JIT_R0, 1), node);
    jit_patch_at(jit_bgtr_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bgti_u(JIT_R0, 1), node);
    jit_patch_at(jit_bner(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bnei(JIT_R0, 1), node);
    jit_patch_at(jit_bmsr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bmsi(JIT_R0, 1), node);
    jit_patch_at(jit_bmcr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bmci(JIT_R0, 1), node);
    jit_patch_at(jit_boaddr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_boaddi(JIT_R0, 1), node);
    jit_patch_at(jit_boaddr_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_boaddi_u(JIT_R0, 1), node);
    jit_patch_at(jit_bxaddr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bxaddi(JIT_R0, 1), node);
    jit_patch_at(jit_bxaddr_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bxaddi_u(JIT_R0, 1), node);
    jit_patch_at(jit_bosubr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bosubi(JIT_R0, 1), node);
    jit_patch_at(jit_bosubr_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bosubi_u(JIT_R0, 1), node);
    jit_patch_at(jit_bxsubr(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bxsubi(JIT_R0, 1), node);
    jit_patch_at(jit_bxsubr_u(JIT_R0, JIT_R1), node);
    jit_patch_at(jit_bxsubi_u(JIT_R0, 1), node);
    jit_addr_f(JIT_F0, JIT_F0, JIT_F1);
    jit_addi_f(JIT_F0, JIT_F0, 1.0);
    jit_subr_f(JIT_F0, JIT_F0, JIT_F1);
    jit_subi_f(JIT_F0, JIT_F0, 1.0);
    jit_mulr_f(JIT_F0, JIT_F0, JIT_F1);
    jit_muli_f(JIT_F0, JIT_F0, 1.0);
    jit_negr_f(JIT_F0, JIT_F1);
    jit_absr_f(JIT_F0, JIT_F1);
    jit_ltr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_lti_f(JIT_R0, JIT_F0, 1.0);
    jit_ler_f(JIT_R0, JIT_F0, JIT_F1);
    jit_lei_f(JIT_R0, JIT_F0, 1.0);
    jit_eqr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_eqi_f(JIT_R0, JIT_F0, 1.0);
    jit_ger_f(JIT_R0, JIT_F0, JIT_F1);
    jit_gei_f(JIT_R0, JIT_F0, 1.0);
    jit_ltr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_lti_f(JIT_R0, JIT_F0, 1.0);
    jit_ner_f(JIT_R0, JIT_F0, JIT_F1);
    jit_nei_f(JIT_R0, JIT_F0, 1.0);
    jit_unltr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_unlti_f(JIT_R0, JIT_F0, 1.0);
    jit_unler_f(JIT_R0, JIT_F0, JIT_F1);
    jit_unlei_f(JIT_R0, JIT_F0, 1.0);
    jit_uneqr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_uneqi_f(JIT_R0, JIT_F0, 1.0);
    jit_unger_f(JIT_R0, JIT_F0, JIT_F1);
    jit_ungei_f(JIT_R0, JIT_F0, 1.0);
    jit_unltr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_unlti_f(JIT_R0, JIT_F0, 1.0);
    jit_ltgtr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_ltgti_f(JIT_R0, JIT_F0, 1.0);
    jit_ordr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_ordi_f(JIT_R0, JIT_F0, 1.0);
    jit_unordr_f(JIT_R0, JIT_F0, JIT_F1);
    jit_unordi_f(JIT_R0, JIT_F0, 1.0);
    jit_movr_f(JIT_F0, JIT_F1);
    jit_movi_f(JIT_F0, 1.0);
    jit_extr_f(JIT_F0, JIT_R0);
    jit_extr_d_f(JIT_F0, JIT_F1);
    jit_ldr_f(JIT_F0, JIT_R0);
    jit_ldi_f(JIT_F0, 0x80000000);
    jit_ldxr_f(JIT_F0, JIT_R0, JIT_R1);
    jit_ldxi_f(JIT_F0, JIT_R0, 8);
    jit_str_f(JIT_R0, JIT_F0);
    jit_sti_f(0x80000000, JIT_F0);
    jit_stxr_f(JIT_R1, JIT_R0, JIT_F0);
    jit_stxi_f(8, JIT_R0, JIT_F0);
    jit_patch_at(jit_bltr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_blti_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bler_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_blei_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_beqr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_beqi_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bger_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bgei_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bltr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_blti_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bner_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bnei_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunltr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunlti_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunler_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunlei_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_buneqr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_buneqi_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunger_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bungei_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunltr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunlti_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bltgtr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bltgti_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bordr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bordi_f(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunordr_f(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunordi_f(JIT_F0, 1.0), node);
    jit_addr_d(JIT_F0, JIT_F0, JIT_F1);
    jit_addi_d(JIT_F0, JIT_F0, 1.0);
    jit_subr_d(JIT_F0, JIT_F0, JIT_F1);
    jit_subi_d(JIT_F0, JIT_F0, 1.0);
    jit_mulr_d(JIT_F0, JIT_F0, JIT_F1);
    jit_muli_d(JIT_F0, JIT_F0, 1.0);
    jit_negr_d(JIT_F0, JIT_F1);
    jit_absr_d(JIT_F0, JIT_F1);
    jit_ltr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_lti_d(JIT_R0, JIT_F0, 1.0);
    jit_ler_d(JIT_R0, JIT_F0, JIT_F1);
    jit_lei_d(JIT_R0, JIT_F0, 1.0);
    jit_eqr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_eqi_d(JIT_R0, JIT_F0, 1.0);
    jit_ger_d(JIT_R0, JIT_F0, JIT_F1);
    jit_gei_d(JIT_R0, JIT_F0, 1.0);
    jit_ltr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_lti_d(JIT_R0, JIT_F0, 1.0);
    jit_ner_d(JIT_R0, JIT_F0, JIT_F1);
    jit_nei_d(JIT_R0, JIT_F0, 1.0);
    jit_unltr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_unlti_d(JIT_R0, JIT_F0, 1.0);
    jit_unler_d(JIT_R0, JIT_F0, JIT_F1);
    jit_unlei_d(JIT_R0, JIT_F0, 1.0);
    jit_uneqr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_uneqi_d(JIT_R0, JIT_F0, 1.0);
    jit_unger_d(JIT_R0, JIT_F0, JIT_F1);
    jit_ungei_d(JIT_R0, JIT_F0, 1.0);
    jit_unltr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_unlti_d(JIT_R0, JIT_F0, 1.0);
    jit_ltgtr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_ltgti_d(JIT_R0, JIT_F0, 1.0);
    jit_ordr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_ordi_d(JIT_R0, JIT_F0, 1.0);
    jit_unordr_d(JIT_R0, JIT_F0, JIT_F1);
    jit_unordi_d(JIT_R0, JIT_F0, 1.0);
    jit_movr_d(JIT_F0, JIT_F1);
    jit_movi_d(JIT_F0, 1.0);
    jit_extr_d(JIT_F0, JIT_R0);
    jit_extr_f_d(JIT_F0, JIT_F1);
    jit_ldr_d(JIT_F0, JIT_R0);
    jit_ldi_d(JIT_F0, 0x80000000);
    jit_ldxr_d(JIT_F0, JIT_R0, JIT_R1);
    jit_ldxi_d(JIT_F0, JIT_R0, 8);
    jit_str_d(JIT_R0, JIT_F0);
    jit_sti_d(0x80000000, JIT_F0);
    jit_stxr_d(JIT_R1, JIT_R0, JIT_F0);
    jit_stxi_d(8, JIT_R0, JIT_F0);
    jit_patch_at(jit_bltr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_blti_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bler_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_blei_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_beqr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_beqi_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bger_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bgei_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bltr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_blti_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bner_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bnei_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunltr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunlti_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunler_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunlei_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_buneqr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_buneqi_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunger_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bungei_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunltr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunlti_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bltgtr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bltgti_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bordr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bordi_d(JIT_F0, 1.0), node);
    jit_patch_at(jit_bunordr_d(JIT_F0, JIT_F1), node);
    jit_patch_at(jit_bunordi_d(JIT_F0, 1.0), node);
    jit_callr(JIT_R0);
    jit_calli((jit_pointer_t)0x80000000);
    jit_jmpr(JIT_R0);
    jit_patch_at(jit_jmpi(), node);
#  endif

#  if 0
    jit_movi(JIT_R1, 10);
    jit_movi(JIT_R2, 2);
    jit_divr(JIT_R0, JIT_R1, JIT_R2);
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"10/2 = %d\n");
    jit_pushargr(JIT_R0);
    jit_finishi(printf);
#  endif

#  if 0
    jit_movi(JIT_R0, 0);
    jit_movi(JIT_R1, 1);
    jit_movi(JIT_R2, 2);
    jit_movi(JIT_V0, 3);
    jit_movi(JIT_V1, 4);
    jit_movi(JIT_V2, 5);
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"%d:%d %d:%d %d:%d %d:%d %d:%d %d:%d\n");
    jit_pushargr(JIT_R0);
    jit_pushargi(0);
    jit_pushargr(JIT_R1);
    jit_pushargi(1);
    jit_pushargr(JIT_R2);
    jit_pushargi(2);
    jit_pushargr(JIT_V0);
    jit_pushargi(3);
    jit_pushargr(JIT_V1);
    jit_pushargi(4);
    jit_pushargr(JIT_V2);
    jit_pushargi(5);
    jit_finishi(printf);
#  endif

#  if 0
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"hello world!\n");
    jit_finishi(printf);
#  endif

#  if 0
    node = jit_movi(JIT_R0, 0);
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"%p\n");
    jit_pushargr(JIT_R0);
    jit_finishi(printf);
    jit_patch(node);
#  endif

#  if 0
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"%d %d %d\n");
    jit_pushargi(1);
    jit_pushargi(1);
    jit_pushargi(1);
    jit_finishi(printf);
#  endif

#  if 0
    jit_movi_d(JIT_F0, 0.0);
    jit_movi_d(JIT_F1, 1.0);
    node = jit_bungtr_d(JIT_F0, JIT_F1);
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"no branch\n");
    jit_finishi(printf);
    done = jit_jmpi();
    jit_patch(node);
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"branch\n");
    jit_finishi(printf);
    jit_patch(done);
#  endif

#  if 0
    jit_movi_d(JIT_F0, 1.0);
    jit_movi_d(JIT_F1, -1.0);
    jit_negr_d(JIT_F0, JIT_F0);
    jit_negr_d(JIT_F1, JIT_F1);

    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"%f %f\n");
    jit_pushargr_d(JIT_F0);
    jit_pushargr_d(JIT_F1);
    jit_finishi(printf);
#  endif

#  if 1
    /* minor check for hardfp arm abi */
    jit_movi_d(JIT_F0, 4.0);
    jit_prepare(jit_call_default);
    jit_pushargr_d(JIT_F0);
    jit_finishi(sqrt);
    jit_retval_d(JIT_F0);

    /* check proper call to varargs function in arm vfp abi and also
     * that it uses an even numbered gpr register for the double argument */
    jit_prepare(jit_call_varargs);
    jit_pushargi((jit_word_t)"%f\n");
    jit_pushargr_d(JIT_F0);
    jit_finishi(printf);
#  endif

    jit_ret();

#else
    eobject_t		*root;
    eint32_t		 thread;
    jit_node_t		*node_segv;
    jit_node_t		*node_catch;
    jit_node_t		*node_except;
    jit_node_t		*node_execute;
    jit_node_t		*node_reenter;

    init_jit();

#if 0
    jit_cpu.sse2 = 0;
#endif

#if DISASSEMBLER
    if (cfg_verbose > 1)
	init_debug();
#endif

    evector(&root_vector, t_void, 8, 1);
    eadd_root((eobject_t *)&root_vector);
    root = root_vector->v.ptr;

    jit_new_state(root + 0);
    _jit = root[0];

    /* forward jump references */
    evector((evector_t **)(root + 1), t_word, 32, 1);
    patch_vector = root[1];
    patches = patch_vector->v.obj;

    /* function scope label addresses */
    evector((evector_t **)(root + 2), t_word, 32, 1);
    label_vector = root[2];
    labels = label_vector->v.obj;

    /* switch table references */
    evector((evector_t **)(root + 3), t_word, 8, 1);
    table_vector = root[3];
    tables = table_vector->v.obj;

    /* patch offsets for forward defined functions */
    evector((evector_t **)(root + 4), t_word, 32, 1);
    call_vector = root[4];
    calls = call_vector->v.obj;

    /* patch offsets for forward defined methods */
    evector((evector_t **)(root + 5), t_word, 32, 1);
    method_vector = root[5];
    methods = method_vector->v.obj;

    /* save switch hash tables for prettier debug output even */
    if (cfg_verbose) {
	enew_hash(root + 6, H_pointer, 5);
	eswitch_hash = root[6];
    }

    jit_prolog();
    thread = jit_arg();
    jit_getarg(JIT_V0, thread);

    /* call sigsetjmp when starting thread */
    jit_prepare(jit_call_default);
    jit_addi(JIT_R0, JIT_V0, offsetof(ethread_t, env));
    jit_pushargr(JIT_R0);
    jit_pushargi(1);
#  if defined(__linux__)
    jit_finishi(__sigsetjmp);
#  else
    jit_finishi(sigsetjmp);
#  endif

    jit_retval_i(JIT_R0);
    /* jump to check for sigsegv if coming from siglongjmp,
     * that is, if sigsetjmp did not return zero */
    node_segv = jit_bnei(JIT_R0, 0);
#  if HAVE_THREAD
    jit_prepare(jit_call_default);
    jit_pushargi((eword_t)&ethread_mutex);
    jit_finishi(pthread_mutex_unlock);
#  endif
    /* jump to start of thread code */
    node_execute = jit_jmpi();

    jit_patch(node_segv);

    /* keep value in R1 */
    jit_ldxi_i(JIT_R1, JIT_V0, offsetof(ethread_t, except));
    /* jump to check try/catch if not coming from sigsegv signal handler */
    node_catch = jit_bnei(JIT_R1, except_segmentation_violation);

    jit_prepare(jit_call_default);
    jit_ldxi(JIT_R0, JIT_V0, offsetof(ethread_t, eip));
    jit_pushargr(JIT_R0);
    jit_pushargi((eword_t)"segmentation violation");
    jit_finishi(eabort);

    jit_patch(node_catch);

    /* keep value in R2 */
    jit_ldxi_i(JIT_R2, JIT_V0, offsetof(ethread_t, tryoff));
    /* jump to reenter code if there is a handler, i.e.
     * thread_self->tryoff >= 0 */
    node_reenter = jit_bgei(JIT_R2, 0);
    /* jump to code to not print exception name if there isn't an
     * entry in the exceptions string vector */
    node_except = jit_bgei_u(JIT_R1, except_unhandled_exception);
    jit_prepare(jit_call_default);
    jit_ldxi(JIT_R0, JIT_V0, offsetof(ethread_t, eip));
    jit_pushargr(JIT_R0);
    jit_muli(JIT_R0, JIT_R1, sizeof(char *));
    jit_movi(JIT_R2, (eword_t)exceptions);
    jit_ldxr(JIT_R0, JIT_R2, JIT_R0);
    jit_pushargr(JIT_R0);
    jit_finishi(eabort);

    jit_patch(node_except);
    jit_prepare(jit_call_default);
    jit_ldxi(JIT_R0, JIT_V0, offsetof(ethread_t, eip));
    jit_pushargr(JIT_R0);
    jit_pushargi((eword_t)"unknown");
    jit_finishi(eabort);

	/* reenter jit after exception */
    jit_patch(node_reenter);
    /* r0.t = t_int */
    jit_movi(JIT_R0, t_int);
    jit_stxi_i(offsetof(ethread_t, r0) + offsetof(evalue_t, t),
	       JIT_V0, JIT_R0);
    /* r0.v.i = thread->except */
#  if __WORDSIZE == 32
    jit_stxi(offsetof(ethread_t, r0) +
	     offsetof(evalue_t, v.i) + offsetof(edata_t, s.l),
	     JIT_V0, JIT_R1);
    jit_movi(JIT_R0, 0);
    jit_stxi(offsetof(ethread_t, r0) +
	     offsetof(evalue_t, v.i) + offsetof(edata_t, s.h),
	     JIT_V0, JIT_R0);
#  else
    jit_stxi(offsetof(ethread_t, r0) + offsetof(evalue_t, v.i),
	     JIT_V0, JIT_R1);
    jit_movi(JIT_R0, 0);
#  endif
    /* thread->except = except_nothing */
    jit_stxi_i(offsetof(ethread_t, except), JIT_V0, JIT_R0);

    /* jump to catch handler, in thread->ess[thread->tryoff + 3].v.o */
    jit_addi(JIT_R0, JIT_R2, 3);
    jit_muli(JIT_R0, JIT_R0, sizeof(evalue_t));
    jit_addi(JIT_R0, JIT_R0, offsetof(evalue_t, v.o));
    jit_ldxi(JIT_R1, JIT_V0, offsetof(ethread_t, ess));
    jit_ldxr(JIT_R0, JIT_R1, JIT_R0);
    jit_jmpr(JIT_R0);

	/* start from here if starting thread */
    jit_patch(node_execute);

    /* jump to start of the thread routine, that should be just
     * after this jump opcode for the main thread */
    jit_ldxi(JIT_R0, JIT_V0, offsetof(ethread_t, eip));
    jit_jmpr(JIT_R0);
#endif
}

void
finish_emit(void)
{
#if SIMPLE_JIT_DEBUG
    jit_emit();

    jit_print();
    ewrite(std_output, "---\n", 4);
    eflush(std_output);

    the_code = _jit->code;
    the_end = _jit->pc.uc;

    edisassemble(the_code, the_end - the_code);

    finish_debug();
#else
    eint32_t		 index;
    eint32_t		 offset;
    efunction_t		*lambda;
    erecord_t		*record;
    esymbol_t		*symbol;

    /* make forward function calls patcheable */
    ecs[0] = fn_root;
    for (offset = 1; offset < ecs_idx; offset++) {
	symbol = ecs[offset];
	lambda = symbol->value;
	if (lambda->prolog)
	    ecs[offset] = lambda;
	else {
	    if (!efunction_p(lambda) || !lambda->defined)
		eerror("function '%p' not defined", symbol->name);
	    else
		ewarn("function '%p' defined but not used", symbol->name);
	    /* not compiled because it was not called or referenced */
	    ecs[offset] = null;
	}
    }

    /* make forward method calls patcheable */
    for (offset = 0; offset < ets_idx; offset++) {
	record = record_vector[offset];
	assert(offset == (record->type & ~t_pointer));
	if (record->methods) {
	    for (index = 0; index < record->methods->count; index++) {
		symbol = ets[offset].methods[index].code;
		lambda = symbol->value;
		if (!efunction_p(lambda) || !lambda->defined)
		    eerror("method '%p.%p' not defined",
			   record->name->name,
			   emethod_name(record, symbol));
		ets[offset].methods[index].code = lambda;
	    }
	}
    }

    /* patch forward function calls */
    for (offset = 0; offset < call_vector->offset; offset += 2) {
	lambda = ecs[calls[offset + 1]];
	jit_patch_at((jit_node_t *)calls[offset], lambda->prolog);
    }

    /* patch forward method methods */
    for (offset = 0; offset < method_vector->offset; offset += 3) {
	lambda = ets[methods[offset + 2]].methods[methods[offset + 1]].code;
	jit_patch_at((jit_node_t *)methods[offset], lambda->prolog);
    }

    jit_emit();
    ethread_exit_code = (eobject_t)thread_exit_label->u.w;
    the_code = _jit->code;
    the_end = _jit->pc.uc;

    /* patch switches */
    /* FIXME need a better logic to patch external data or jump tables */
    for (offset = 0; offset < table_vector->offset; offset++)
	table_patch(els[tables[offset]]);

    /* resolve functions */
    for (offset = 0; offset < ecs_idx; offset++) {
	if ((lambda = ecs[offset])) {
	    emit_note(lambda);
	    ecs[offset] = lambda->prolog;
	    lambda->note = 1;
	}
    }

    /* resolve methods */
    for (offset = 0; offset < ets_idx; offset++) {
	record = record_vector[offset];
	if (record->methods) {
	    for (index = 0; index < record->methods->count; index++) {
		lambda = ets[offset].methods[index].code;
		if (!lambda->note) {
		    emit_note(lambda);
		    lambda->note = 1;
		}
		ets[offset].methods[index].code = lambda->prolog;
	    }
	}
    }

#if DEBUG
    if (cfg_verbose > 2) {
	ewrite(std_output, "---\n", 4);
	jit_print();
	ewrite(std_output, "---\n", 4);
	eflush(std_output);
    }
#endif

    if (jit_missing_count)
	eerror("new jit not yet functional");

    erem_root((eobject_t *)&root_vector);

#if DISASSEMBLER
    if (cfg_verbose > 1)
	finish_debug();
#endif
#endif
}

eobject_t
evm(ethread_t *thread)
{
#if SIMPLE_JIT_DEBUG
    jit_t		jit;

    if (jit_missing_count)
	eerror("new jit not yet functional");

    jit = (jit_t)the_code;

#  if 1
    return (call_jit(jit, thread));
#  else
    return (null);
#  endif
#else
    jit_t		jit;

    if (thread != tmain) {
	evalue_t	*v;

	thread_self = thread;
	empcache_setup();
	for (v = thread->esp - 3; v >= thread->ess; v--) {
	    switch (v->t) {
		case t_mpz:	enew_mpz(&v->v.o, v->v.o);	break;
		case t_mpq:	enew_mpq(&v->v.o, v->v.o);	break;
		case t_mpr:	enew_mpr(&v->v.o, v->v.o);	break;
		case t_cqq:	enew_cqq(&v->v.o, v->v.o);	break;
		case t_mpc:	enew_mpc(&v->v.o, v->v.o);	break;
		default:					break;
	    }
	}
    }

    /* in case gc is running and will try to inspect this thread stack */
    ethreads_lock();
    thread->running = 1;
    thread->except = except_nothing;

    jit = (jit_t)the_code;

    return (call_jit(jit, thread));
#endif
}

void
esearch_instruction_pointer(void)
{
    euint8_t	*eip;
#if HAVE_EXECINFO_H
    eint32_t	 length;
    eint32_t	 offset;
    eobject_t	 frames[32];

    length = backtrace(frames, sizeof(frames) / sizeof(frames[0]));
    for (offset = 0; offset < length; offset++) {
	eip = frames[offset];
	if (eip >= the_code && eip < the_end) {
	    thread_self->eip = eip;
	    break;
	}
    }
#else	/* this may still have problems if not compiled with
	 * -fno-omit-frame-pointer on i686 */
    /* __builtin_return_address() and __builtin_frame_address()
     * require an integer constant argument */
#  define search_instruction_pointer(number)				\
    do {								\
	eip = __builtin_return_address(number);				\
	if (eip == null || __builtin_frame_address(number) == null)	\
	    goto fail;							\
	if (eip >= (eobject_t)the_code && eip < the_end)		\
	    goto done;							\
    } while (0)
    search_instruction_pointer(0);
#  if !defined(__x86_64__)
    search_instruction_pointer(1);
    search_instruction_pointer(2);
    search_instruction_pointer(3);
    search_instruction_pointer(4);
    search_instruction_pointer(5);
    search_instruction_pointer(6);
    search_instruction_pointer(7);
    search_instruction_pointer(8);
    search_instruction_pointer(9);
    search_instruction_pointer(10);
    search_instruction_pointer(11);
    search_instruction_pointer(12);
    search_instruction_pointer(13);
    search_instruction_pointer(14);
    search_instruction_pointer(15);
    search_instruction_pointer(16);
    search_instruction_pointer(17);
    search_instruction_pointer(18);
    search_instruction_pointer(19);
    search_instruction_pointer(20);
    search_instruction_pointer(21);
    search_instruction_pointer(22);
    search_instruction_pointer(23);
    search_instruction_pointer(24);
    search_instruction_pointer(25);
    search_instruction_pointer(26);
    search_instruction_pointer(27);
    search_instruction_pointer(28);
    search_instruction_pointer(29);
    search_instruction_pointer(30);
    search_instruction_pointer(31);
    /* Greed is Good! (the more stack frames the better) */
#  endif
fail:
    /* will probably print a bogus backtrace with an early address */
    return;
done:
    thread_self->eip = eip;
#endif
}

ebool_t
ejit_pointer_p(eobject_t value)
{
    return ((jit_uint8_t *)value >= the_code && (jit_uint8_t *)value < the_end);
}

static void
emit_note(efunction_t *function)
{
    east_list_t		*list;
    east_node_t		*node;
    enote_t		*note;
    jit_node_t		*label;
    jit_node_t		*prolog;
    jit_node_t		*epilog;

    if (cfg_verbose) {
	ewrite_function(function);
	if (cfg_verbose > 3)
	    ewrite_code(function->code);
    }

    prolog = function->prolog;
    epilog = function->epilog;

    enew_note((eobject_t *)&note, (eint8_t *)prolog->u.w,
	      function->name ? function->name->name : null);
    eset_note(note, epilog->u.w - prolog->u.w);

    for (list = function->code; list; list = east_next(list)) {
	node = east_node(list);
	/* if not "optimized", sequential notes may appear,
	 * and a jit_note object not allocated */
	if (node->code == eop_note && node->cnote.note) {
	    label = node->cnote.note;
	    eannotate(note, node->cnote.name, node->cnote.line,
		      label->u.w - prolog->u.w);
	}
    }

    efinish_note(note);
    function->prolog = prolog->u.p;
    function->epilog = epilog->u.p;

#if DISASSEMBLER
    if (cfg_verbose > 1)
	edisassemble(function->prolog, epilog->u.w - prolog->u.w);
#endif
}

static void
call_vv(void(*vv)(void))
{
    (void)jit_calli(vv);
}

static void
call_vi(void(*vi)(eint32_t), eint32_t a0)
{
    jit_prepare(jit_call_default);
    jit_pushargi(a0);
    jit_finishi(vi);
}

static void
call_pi(eobject_t(*pi)(eint32_t), eint32_t a0)
{
    jit_prepare(jit_call_default);
    jit_pushargi(a0);
    jit_finishi(pi);
}

static void
call_vii(void(*vii)(eint32_t, eint32_t), eint32_t a0, eint32_t a1)
{
    jit_prepare(jit_call_default);
    jit_pushargi(a0);
    jit_pushargi(a1);
    jit_finishi(vii);
}

static void
call_pii(eobject_t(*pii)(eint32_t, eint32_t), eint32_t a0, eint32_t a1)
{
    jit_prepare(jit_call_default);
    jit_pushargi(a0);
    jit_pushargi(a1);
    jit_finishi(pii);
}

static void
call_viii(void(*viii)(eint32_t, eint32_t, eint32_t),
	  eint32_t a0, eint32_t a1, eint32_t a2)
{
    jit_prepare(jit_call_default);
    jit_pushargi(a0);
    jit_pushargi(a1);
    jit_pushargi(a2);
    jit_finishi(viii);
}

static void
call_viiii(void(*viiii)(eint32_t, eint32_t, eint32_t, eint32_t),
	   eint32_t a0, eint32_t a1, eint32_t a2, eint32_t a3)
{
    jit_prepare(jit_call_default);
    jit_pushargi(a0);
    jit_pushargi(a1);
    jit_pushargi(a2);
    jit_pushargi(a3);
    jit_finishi(viiii);
}

static void
emit_enter(east_node_t *node)
{
    eint32_t		 size;
    jit_node_t		*instr;
    jit_node_t		*vector;

    /* FIXME only needs to coerce arguments if called via apply as in other
     * cases it is already correct; so far apply allows pointers to different
     * function types; only requirement is matching number of arguments.
     */
    if ((size = (node->center.acount + node->center.lcount) << 2))
	vector = jit_data(&node->center.types, size);
    else
	vector = null;
    jit_prepare(jit_call_default);
    jit_pushargi(node->center.local);
    jit_pushargi(node->center.extra);
    jit_pushargi(node->center.acount);
    jit_pushargi(node->center.lcount);
    if (vector) {
	instr = jit_movi(JIT_R0, (jit_word_t)vector);
	jit_patch_at(instr, vector);
	jit_pushargr(JIT_R0);
    }
    else	    
	jit_pushargi(0);
    jit_finishi(evm_enter);
}

static void
emit_vnewi(east_node_t *node)
{
    eint32_t		 size;
    jit_node_t		*instr;
    jit_node_t		*vector;

    if ((size = node->cvector.rank << 2))
	vector = jit_data(&node->cvector.dims, size);
    else
	vector = null;
    jit_prepare(jit_call_default);
    jit_pushargi(node->cvector.type);
    jit_pushargi(node->cvector.length);
    jit_pushargi(node->cvector.rank);
    if (vector) {
	instr = jit_movi(JIT_R0, (jit_word_t)vector);
	jit_patch_at(instr, vector);
	jit_pushargr(JIT_R0);
    }
    else	    
	jit_pushargi(0);
    jit_finishi(evm_vnewi);
}

static void
emit_try(east_list_t *list)
{
    east_node_t		*node;
    jit_node_t		*protect;
    jit_node_t		*address;

    /* pass as argument the offset after the jump to catch table */
    jit_prepare(jit_call_default);
    address = jit_movi(JIT_R0, 0);
    jit_pushargr(JIT_R0);
    jit_finishi(evm_try);

    /* jump to start of protected code */
    protect = jit_jmpi();

    assert(list != null);
    node = east_node(list);
    assert(node->code == eop_j);

    /* arrival on exception (evm_try argument) */
    jit_patch(address);

    /* generate jump to the catch handler */
    emit_branch(node->clabel.label);

    /* start protected code */
    jit_patch(protect);
}

static void
emit_stackref(eint32_t offset, eint32_t type)
{
    /* r0.t = type */
    jit_movi(JIT_R0, type);
    jit_stxi_i(offsetof(ethread_t, r0) + offsetof(evalue_t, t), JIT_V0, JIT_R0);
    /* r0.v.o = bp + offset */
    jit_ldxi(JIT_R0, JIT_V0, offsetof(ethread_t, ebp));
    jit_addi(JIT_R0, JIT_R0, offset * sizeof(evalue_t));
    jit_stxi(offsetof(ethread_t, r0) + offsetof(evalue_t, v.o),
	     JIT_V0, JIT_R0);
}

static void
record_forward_call(jit_node_t *call, eint32_t offset)
{
    if (call_vector->offset >= call_vector->length) {
	erenew_vector(call_vector, call_vector->length + 32);
	calls = call_vector->v.obj;
    }
    calls[call_vector->offset++] = (jit_word_t)call;
    calls[call_vector->offset++] = offset;
}

static jit_node_t *
emit_frame(void)
{
    jit_node_t		*address;

    /* return address */
    address = jit_movi(JIT_R2, 0);

    /* sp += 2 */
    jit_ldxi(JIT_R1, JIT_V0, offsetof(ethread_t, esp));
    jit_addi(JIT_R0, JIT_R1, sizeof(evalue_t) * 2);
    jit_stxi(offsetof(ethread_t, esp), JIT_V0, JIT_R0);

    /* FIXME currently vm stack slots are quite huge due to
     * having place for type information, and also storing
     * a complex double by value; if this is not changed,
     * can store two pointers in a vm stack slot, i.e. return
     * address and saved bp pointer
     */

    /* sp[-2].t = sp[-1].t = t_void */
    jit_movi(JIT_R0, t_void);
    jit_stxi_i(sizeof(evalue_t) + offsetof(evalue_t, t), JIT_R1, JIT_R0);
    jit_stxi_i(offsetof(evalue_t, t), JIT_R1, JIT_R0);
    /* sp[-2].v.o = return address */
    jit_stxi(offsetof(evalue_t, v.o), JIT_R1, JIT_R2);

    /* sp[-1].v.o = bp */
    jit_ldxi(JIT_R0, JIT_V0, offsetof(ethread_t, ebp));
    jit_stxi(sizeof(evalue_t) + offsetof(evalue_t, v.o), JIT_R1, JIT_R0);

    /* bp = sp - 1 */
    jit_addi(JIT_R0, JIT_R1, sizeof(evalue_t));
    jit_stxi(offsetof(ethread_t, ebp), JIT_V0, JIT_R0);

    return (address);
}

static void
emit_call(eint32_t offset)
{
    jit_node_t		*call;
    efunction_t		*lambda;
    esymbol_t		*symbol;
    jit_node_t		*address;

    /* save stack frame */
    address = emit_frame();

    call = jit_jmpi();
    symbol = ecs[offset];
    lambda = symbol->value;
    if (!efunction_p(lambda) || !lambda->defined)
	eerror("function '%p' not defined", symbol->name);
    if (lambda->prolog)
	jit_patch_at(call, lambda->prolog);
    else
	record_forward_call(call, offset);

    /* where ret* will land */
    jit_patch(address);
}

static void
emit_method_call(eint32_t offset, eint32_t type)
{
    jit_node_t		*call;
    jit_node_t		*kind;
    jit_node_t		*method;
    jit_node_t		*parent;

    /* if (r0.t >= ets_idx) goto label_class; */
    jit_ldxi_i(JIT_R0, JIT_V0,
	       offsetof(ethread_t, r0) + offsetof(evalue_t, t));
    kind = jit_bgei_u(JIT_R0, ets_idx);

    /* V0 = ets + r0.t */
    jit_movi(JIT_V1, (jit_word_t)ets);
    jit_muli(JIT_R0, JIT_R0, sizeof(ertti_t));
    jit_addr(JIT_V1, JIT_V1, JIT_R0);
    /* if (ets[r0.t].nmethod <= offset) goto label_method; */
    jit_ldxi_i(JIT_R0, JIT_V1, offsetof(ertti_t, nmethod));
    method = jit_blei_u(JIT_R0, offset);

    /* if (ets[r0.t].methods[offset].type != type) goto label_parent; */
    jit_ldxi(JIT_V1, JIT_V1, offsetof(ertti_t, methods));
    jit_ldxi_i(JIT_R0, JIT_V1, offset * sizeof(emethod_t) +
	       offsetof(emethod_t, type));
    parent = jit_bnei(JIT_R0, type);

    /* save stack frame - clobbers r0, r1 and r2 */
    call = emit_frame();

    /* R0 = ets[r0.t].methods[atom->offset].code */
    jit_ldxi(JIT_R0, JIT_V1, offset * sizeof(emethod_t) +
	     offsetof(emethod_t, code));
    /* indirect call to virtual method associated with r0.t type */
    jit_jmpr(JIT_R0);

    /* error handling */
    jit_patch(kind);
    jit_patch(method);
    jit_patch(parent);
    call_vi(evm_raise, except_type_mismatch);

    /* where ret* will land */
    jit_patch(call);
}

static void
record_forward_method(jit_node_t *call, eword_t offset, eword_t type)
{
    if (method_vector->offset + 3 >= method_vector->length) {
	erenew_vector(method_vector, method_vector->length + 32);
	methods = method_vector->v.obj;
    }
    methods[method_vector->offset++] = (jit_word_t)call;
    methods[method_vector->offset++] = offset;
    methods[method_vector->offset++] = type;
}

static void
emit_explicit_call(eint32_t offset, eint32_t type)
{
    jit_node_t		*call;
    jit_node_t		*kind;
    jit_node_t		*instr;
    jit_node_t		*label;
    efunction_t		*lambda;
    esymbol_t		*symbol;
    jit_node_t		*address;
    jit_node_t		*inherit;

    /* if (r0.t >= ets_idx) goto label_class; */
    jit_ldxi_i(JIT_R0, JIT_V0,
	       offsetof(ethread_t, r0) + offsetof(evalue_t, t));
    kind = jit_bgei_u(JIT_R0, ets_idx);

    jit_movi(JIT_V1, (jit_word_t)ets);

    /* label_loop: */
    label = jit_label();
    /* if (R0 == type) goto label_call; */
    call = jit_beqi(JIT_R0, type);
    /* if (R0 == t_void) goto label_inherit; */
    inherit = jit_beqi(JIT_R0, t_void);
    /* R0 = ets[R0].super */
    jit_muli(JIT_R0, JIT_R0, sizeof(ertti_t));
    jit_addi(JIT_R0, JIT_R0, offsetof(ertti_t, super));
    jit_ldxr_i(JIT_R0, JIT_V1, JIT_R0);
    /* goto label_loop */
    instr = jit_jmpi();
    jit_patch_at(instr, label);

    /* call explicit method */
    jit_patch(call);

    /* save stack frame */
    address = emit_frame();

    call = jit_jmpi();
    symbol = ets[type].methods[offset].code;
    lambda = symbol->value;
    if (!efunction_p(lambda) || !lambda->record)
	eerror("method '<undefined>.%p' not defined", symbol->name);
    else if (!lambda->defined)
	eerror("method '%p.%p' not defined",
	       lambda->record->name->name,
	       emethod_name(lambda->record, symbol));
    if (lambda->prolog)
	jit_patch_at(call, lambda->prolog);
    else
	record_forward_method(call, offset, type);

    /* error handling */
    jit_patch(kind);
    jit_patch(inherit);
    call_vi(evm_raise, except_type_mismatch);

    /* where ret* will land */
    jit_patch(address);
}

static eword_t
allocate_forward_label(elabel_t *label)
{
    eword_t		offset;

    /* skip first entry to be able to use a negative value */
    if (label_vector->offset == 0)
	++label_vector->offset;

    if ((offset = label_vector->offset) >= label_vector->length) {
	erenew_vector(label_vector, label_vector->length + 32);
	labels = label_vector->v.obj;
    }
    offset = -offset;
    label->value = efixnum(offset);
    ++label_vector->offset;

    return (-offset);
}

static void
record_forward_patch(jit_node_t *instr, eword_t label)
{
    if (patch_vector->offset >= patch_vector->length) {
	erenew_vector(patch_vector, patch_vector->length + 32);
	patches = patch_vector->v.obj;
    }
    patches[patch_vector->offset++] = label;
    patches[patch_vector->offset++] = (eword_t)instr;
}

static void
emit_branch(elabel_t *label)
{
    jit_node_t		*jump;
    eword_t		 index;

    emit_flush();
    if (label->value == null)	allocate_forward_label(label);
    index = eget_fixnum(label->value);
    jump = jit_jmpi();
    if (index >= 0)		jit_patch_at(jump, labels[index]);
    else			record_forward_patch(jump, -index);
}

static ecode_t
switches(ehash_t *hash, eint32_t offset)
{
    eint32_t		 i;
    ecode_t		 code;
    east_node_t		*node;
    eint32_t		 size;
    eentry_t		*entry;
    eint32_t		*array;
    ecase_t		*cases;
    eint32_t		 index;
    eint32_t		 length;
    eint32_t		 clashes;
    eint_t		 integer;
    evswitch_t		*vswitch;
    ehswitch_t		*hswitch;
    elabel_t		*fallback;
    eint_t		 imin, imax;

    if (cfg_verbose) {
	enew(&entry, entry);
	entry->name = efixnum(offset);
	entry->value = hash;
	eput_hash(eswitch_hash, entry);
    }

    /* default address */
    entry = eget_hash(hash, null);
    assert(entry != null);

    /* fallback is used to fill vswitch unused entries, so that it is
     * not required to check for an invalid 0 distance jump at runtime */
    node = entry->value;
    fallback = node->clabel.label;
    /* remove the 'default' entry as it is no longer used */
    erem_hash(hash, entry);

    /* hash->count is guaranteed to not be zero */
    size = hash->count;
    /* if size is not already a power of two */
    if (size & (size - 1)) {
	for (i = 1 << 30; i >= 0; i >>= 1)
	    if (size & i)
		break;
	size = i << 1;
	/* should only assert on integer wrap */
	assert(size >= hash->count);
    }

    v_check(1);
    enew_object((eobject_t *)&array, t_void,
		(size / 32 + 1) * sizeof(eint32_t));
    v_push(array);

    /* set all bits and simplify code below */
    size -= 1;

    /* check min/max values, and if there are any clashes */
    clashes = 0;
    imax = 0x8000000000000000L;
    imin = 0x7fffffffffffffffL;
    for (i = 0; i < hash->size; i++) {
	for (entry = hash->entries[i]; entry; entry = entry->next) {
	    integer = eget_integer(entry->name);
	    if (integer > imax)
		imax = integer;
	    if (integer < imin)
		imin = integer;
	    integer &= size;
	    if (array[integer / 32] & (1 << (integer & 31)))
		++clashes;
	    else
		array[integer / 32] |= 1 << (integer & 31);
	}
    }

    if (clashes == 0 && imax - imin <= size) {
	/* faster case */
	enew_object((eobject_t *)&vswitch, t_void,
		    sizeof(evswitch_t) + sizeof(eobject_t) * size);
	v_top() = vswitch;
	vswitch->imin = imin;
	vswitch->imax = imax;
	vswitch->mask = size;
	/* dont need to check for a zero distance jump at runtime */
	for (i = 0; i <= size; i++)
	    vswitch->cases[i] = fallback;
	for (i = 0; i < hash->size; i++) {
	    for (entry = hash->entries[i]; entry; entry = entry->next) {
		integer = eget_integer(entry->name);
		index = integer & size;
		node = entry->value;
		vswitch->cases[index] = node->clabel.label;
	    }
	}
	code = eop_jv;
	els[offset] = vswitch;
    }
    else {
	/* slower case */
	enew_object((eobject_t *)&hswitch, t_switch, sizeof(ehswitch_t) +
		    sizeof(eobject_t) * size);
	v_top() = hswitch;
	hswitch->imin = imin;
	hswitch->imax = imax;
	hswitch->mask = size;
	for (i = 0; i < hash->size; i++) {
	    for (entry = hash->entries[i]; entry; entry = entry->next) {
		integer = eget_integer(entry->name);
		index = integer & size;
		if ((cases = hswitch->cases[index]) == null) {
		    enew_object((eobject_t *)&cases,
				t_resize | t_void, sizeof(ecase_t));
		    hswitch->cases[index] = cases;
		    length = 0;
		}
		else {
		    /* a clash */
		    length = eobject_length(cases) + sizeof(ecase_t);
		    eresize_object((eobject_t *)(hswitch->cases + index),
				   length);
		    cases = hswitch->cases[index];
		    length = length / sizeof(ecase_t) - 1;
		}
		cases[length].value = integer;
		node = entry->value;
		cases[length].label = node->clabel.label;
	    }
	}
	code = eop_jh;
	els[offset] = hswitch;
    }
    v_dec();

    return (code);
}

static void
record_table_patch(eword_t offset)
{
    if (table_vector->offset >= table_vector->length) {
	erenew_vector(table_vector, table_vector->length + 8);
	tables = table_vector->v.obj;
    }
    tables[table_vector->offset++] = offset;
}

static void
emit_table_branch(ehash_t *hash, eint32_t offset)
{
    east_node_t		*node;
    eentry_t		*entry;
    elabel_t		*label;
    eword_t		 index;
    jit_node_t		*node_jump;

    /* reference labels */
    for (index = 0; index < hash->size; index++) {
	for (entry = hash->entries[index]; entry; entry = entry->next) {
	    node = entry->value;
	    label = node->clabel.label;
	    /* should always be null */
	    if (label->value == null)
		(void)allocate_forward_label(label);
	}
    }

    /* call function to choose jump address */
    jit_prepare(jit_call_default);
    jit_pushargi(offset);
    if (switches(hash, offset) == eop_jv)
	jit_finishi(evm_jv);
    else
	jit_finishi(evm_jh);
    jit_retval(JIT_R0);

    /* return is null if no entry found in table, what will cause
     * it to reach a jump to either default entry, or outside of
     * switch, that is the instruction following eop_jh */
    node_jump = jit_beqi(JIT_R0, 0);
    jit_jmpr(JIT_R0);
    jit_patch(node_jump);

    record_table_patch(offset);
}

static void
vswitch_forward(evswitch_t *table)
{
    jit_node_t		*node;
    elabel_t		*label;
    eint32_t		 length;
    eint32_t		 offset;

    length = (eobject_length(table) -
	      offsetof(evswitch_t, cases)) / sizeof(eobject_t);
    for (offset = 0; offset < length; offset++) {
	label = table->cases[offset];
	node = labels[eget_fixnum(label->value)];
	node->flag |= jit_flag_use;
	table->cases[offset] = node;
    }
}

static void
hswitch_forward(ehswitch_t *table)
{
    jit_node_t		*node;
    ecase_t		*acase;
    elabel_t		*label;
    eint32_t		 count;
    eint32_t		 length;
    eint32_t		 offset;

    count = (eobject_length(table) -
	     offsetof(ehswitch_t, cases)) / sizeof(eobject_t);
    for (offset = 0; offset < count; offset++) {
	acase = table->cases[offset];
	length = eobject_length(acase) / sizeof(ecase_t);
	for (; length; length--, acase++) {
	    label = acase->label;
	    node = labels[eget_fixnum(label->value)];
	    node->flag |= jit_flag_use;
	    acase->label = node;
	}
    }
}

static void
table_forward(eobject_t object)
{
    if (etype(object) == t_switch)
	hswitch_forward(object);
    else
	vswitch_forward(object);
}

static void
vswitch_patch(evswitch_t *table)
{
    jit_node_t		*label;
    eint32_t		 length;
    eint32_t		 offset;

    length = (eobject_length(table) -
	      offsetof(evswitch_t, cases)) / sizeof(eobject_t);
    for (offset = 0; offset < length; offset++) {
	label = table->cases[offset];
	table->cases[offset] = (eobject_t)label->u.w;
    }
}

static void
hswitch_patch(ehswitch_t *table)
{
    jit_node_t		*label;
    ecase_t		*acase;
    eint32_t		 count;
    eint32_t		 length;
    eint32_t		 offset;

    count = (eobject_length(table) -
	     offsetof(ehswitch_t, cases)) / sizeof(eobject_t);
    for (offset = 0; offset < count; offset++) {
	acase = table->cases[offset];
	length = eobject_length(acase) / sizeof(ecase_t);
	for (; length; length--, acase++) {
	    label = acase->label;
	    acase->label = (eobject_t)label->u.w;
	}
    }
}

static void
table_patch(eobject_t object)
{
    if (etype(object) == t_switch)
	hswitch_patch(object);
    else
	vswitch_patch(object);
}

static void
zero_sign_extend_reg(eint32_t type, jit_int32_t lreg, jit_int32_t hreg)
{
#if __WORDSIZE == 32
    switch (type) {
	case t_int8:
	    jit_rshi(hreg, lreg, 7);
	    break;
	case t_int16:
	    jit_rshi(hreg, lreg, 15);
	    break;
	case t_int32:
	    jit_rshi(hreg, lreg, 31);
	    break;
	case t_uint8:			case t_uint16:
	case t_uint32:
	    jit_movi(hreg, 0);
	    break;
	case t_int:			case t_uint:
	    break;
	default:
	    abort();
    }
#endif
}

static void
zero_sign_extend(eint32_t type)
{
    zero_sign_extend_reg(type, JIT_V1, JIT_V2);
}

static void
load_ireg_reg_type(jit_int32_t base, eint32_t offset,
		   jit_int32_t lreg, jit_int32_t hreg, eint32_t type)
{
    switch (type) {
	case t_int8:
	    jit_ldxi_c(lreg, base, offset);
	    break;
	case t_uint8:
	    jit_ldxi_uc(lreg, base, offset);
	    break;
	case t_int16:
	    jit_ldxi_s(lreg, base, offset);
	    break;
	case t_uint16:
	    jit_ldxi_us(lreg, base, offset);
	    break;
	case t_int32:
#if __WORDSIZE == 32
	case t_uint32:
#endif
	    jit_ldxi_i(lreg, base, offset);
	    break;
#if __WORDSIZE == 64
	case t_uint32:
	    jit_ldxi_ui(lreg, base, offset);
	    break;
#endif
	default:
#if __WORDSIZE == 32
	    jit_ldxi(lreg, base, offset + offsetof(edata_t, s.l));
	    jit_ldxi(hreg, base, offset + offsetof(edata_t, s.h));
#else
	    jit_ldxi(lreg, base, offset);
#endif
	    break;
    }
}

static void
load_ireg(jit_int32_t base, eint32_t offset)
{
#if __WORDSIZE == 32
    jit_ldxi(JIT_V1, base, offset + offsetof(edata_t, s.l));
    jit_ldxi(JIT_V2, base, offset + offsetof(edata_t, s.h));
#else
    jit_ldxi(JIT_V1, base, offset);
#endif
}

static void
load_ireg_type(jit_int32_t base, eint32_t offset, eint32_t type)
{
    load_ireg_reg_type(base, offset, JIT_V1, JIT_V2, type);
}

static void
load_ireg_implicit(void)
{
    load_ireg(JIT_V0, offsetof(ethread_t, r0) + offsetof(evalue_t, v.i));
}

static void
load_ireg32_implicit(void)
{
    jit_ldxi_i(JIT_V1, JIT_V0, offsetof(ethread_t, r0) +
	       offsetof(evalue_t, v.i) + offsetof(edata_t, s.l));
}

static void
maybe_reload_ireg_implicit(void)
{
    if (!(ejit_sync & ejit_sync_register)) {
	load_ireg_implicit();
	if (!(ejit_sync & ejit_sync_dynamic))
	    ejit_sync |= ejit_sync_register|ejit_sync_int_ext;
    }
}

static void
maybe_reload_ireg32_implicit(void)
{
    if (!(ejit_sync & ejit_sync_register)) {
	assert(ejit_sync & ejit_sync_implicit);
#  if __WORDSIZE == 32
	jit_ldxi(JIT_V1, JIT_V0,
		 offsetof(evalue_t, v.i) + offsetof(edata_t, s.l));
#  else
	load_ireg_implicit();
#  endif
	if (!(ejit_sync & ejit_sync_dynamic))
	    ejit_sync |= ejit_sync_register;
    }
}

static void
maybe_reload_extend_ireg_implicit(eint32_t type)
{
    if (!(ejit_sync & ejit_sync_register)) {
	assert(ejit_sync & ejit_sync_implicit);
	load_ireg_implicit();
	ejit_sync |= ejit_sync_register|ejit_sync_int_ext;
    }
    else if (!(ejit_sync & ejit_sync_int_ext)) {
	zero_sign_extend(type);
	ejit_sync |= ejit_sync_int_ext;
    }
}

static void
store_ireg(jit_int32_t base, eint32_t offset)
{
#if __WORDSIZE == 32
    jit_stxi(offset + offsetof(edata_t, s.l), base, JIT_V1);
    jit_stxi(offset + offsetof(edata_t, s.h), base, JIT_V2);
#else
    jit_stxi(offset, base, JIT_V1);
#endif
}

static void
store_ireg_value_type(jit_int32_t base, eint32_t offset)
{
    jit_movi(JIT_R0, t_int);
    jit_stxi_i(offset + offsetof(evalue_t, t), base, JIT_R0);
    store_ireg(base, offset + offsetof(evalue_t, v.i));
}

static void
store_ireg_implicit(ebool_t type)
{
    if (type)
	store_ireg_value_type(JIT_V0, offsetof(ethread_t, r0));
    else
	store_ireg(JIT_V0, offsetof(ethread_t, r0) + offsetof(evalue_t, v.i));
}

#if __WORDSIZE == 32
static void
store_ireg_implicit_rgz(eint32_t flag, jit_int32_t l, jit_int32_t h)
{
    jit_int32_t		reg;

    if (l == JIT_V1 || h == JIT_V1) {
	if (l == JIT_V2 || h == JIT_V2) {
	    reg = JIT_R0;
	}
	else
	    reg = JIT_V2;
    }
    else
	reg = JIT_V1;
    jit_prepare(jit_call_default);
    jit_movi(reg, t_rgz);
    jit_stxi_i(offsetof(ethread_t, r0) + offsetof(evalue_t, t),
	       JIT_V0, reg);
    jit_addi(reg, JIT_V0,
	     offsetof(ethread_t, qq) + offsetof(__cqq_struct, re) +
	     offsetof(__mpq_struct, _mp_num));
    jit_stxi(offsetof(ethread_t, r0) + offsetof(evalue_t, v.o),
	     JIT_V0, reg);
    jit_pushargr(reg);
    jit_pushargr(l);
    jit_pushargr(h);
    switch (flag) {
	case 0:
	    jit_finishi(empz_set_uu);
	    break;
	case RGZ_signed:
	    jit_finishi(empz_set_ii);
	    break;
	case RGZ_ptr_to_v1:
	    jit_movr(JIT_V1, reg);
	    jit_finishi(empz_set_uu);
	    break;
	case RGZ_ptr_to_v1|RGZ_signed:
	    jit_movr(JIT_V1, reg);
	    jit_finishi(empz_set_ii);
	    break;
	case RGZ_r1_to_v1:
	    jit_movr(JIT_V1, JIT_R1);
	    jit_finishi(empz_set_uu);
	    break;
	case RGZ_r1_to_v1|RGZ_signed:
	    jit_movr(JIT_V1, JIT_R1);
	    jit_finishi(empz_set_ii);
	    break;
	case RGZ_r1_pop_to_v1:
	    jit_subi(JIT_V1, JIT_R1, sizeof(evalue_t));
	    jit_finishi(empz_set_uu);
	    break;
	case RGZ_r1_pop_to_v1|RGZ_signed:
	    jit_subi(JIT_V1, JIT_R1, sizeof(evalue_t));
	    jit_finishi(empz_set_ii);
	    break;
	case RGZ_r2_to_v1:
	    jit_movr(JIT_V1, JIT_R2);
	    jit_finishi(empz_set_uu);
	    break;
	case RGZ_r2_to_v1|RGZ_signed:
	    jit_movr(JIT_V1, JIT_R2);
	    jit_finishi(empz_set_ii);
	    break;
	default:
	    abort();
    }
}
#else
static void
store_ireg_implicit_rgz(eint32_t flag, jit_int32_t r)
{
    jit_int32_t		reg;

    if (r == JIT_V1)
	reg = JIT_R0;
    else
	reg = JIT_V1;
    jit_prepare(jit_call_default);
    jit_movi(reg, t_rgz);
    jit_stxi_i(offsetof(ethread_t, r0) + offsetof(evalue_t, t),
	       JIT_V0, reg);
    jit_addi(reg, JIT_V0,
	     offsetof(ethread_t, qq) + offsetof(__cqq_struct, re) +
	     offsetof(__mpq_struct, _mp_num));
    jit_stxi(offsetof(ethread_t, r0) + offsetof(evalue_t, v.o),
	     JIT_V0, reg);
    jit_pushargr(reg);
    jit_pushargr(r);
    switch (flag) {
	case 0:
	    jit_finishi(mpz_set_ui);
	    break;
	case RGZ_signed:
	    jit_finishi(mpz_set_si);
	    break;
	case RGZ_ptr_to_v1:
	    jit_movr(JIT_V1, reg);
	    jit_finishi(mpz_set_ui);
	    break;
	case RGZ_ptr_to_v1|RGZ_signed:
	    jit_movr(JIT_V1, reg);
	    jit_finishi(mpz_set_si);
	    break;
	case RGZ_r1_to_v1:
	    jit_movr(JIT_V1, JIT_R1);
	    jit_finishi(mpz_set_ui);
	    break;
	case RGZ_r1_to_v1|RGZ_signed:
	    jit_movr(JIT_V1, JIT_R1);
	    jit_finishi(mpz_set_si);
	    break;
	case RGZ_r1_pop_to_v1:
	    jit_subi(JIT_V1, JIT_R1, sizeof(evalue_t));
	    jit_finishi(mpz_set_ui);
	    break;
	case RGZ_r1_pop_to_v1|RGZ_signed:
	    jit_subi(JIT_V1, JIT_R1, sizeof(evalue_t));
	    jit_finishi(mpz_set_si);
	    break;
	case RGZ_r2_to_v1:
	    jit_movr(JIT_V1, JIT_R2);
	    jit_finishi(mpz_set_ui);
	    break;
	case RGZ_r2_to_v1|RGZ_signed:
	    jit_movr(JIT_V1, JIT_R2);
	    jit_finishi(mpz_set_si);
	    break;
	default:
	    abort();
    }
}
#endif

static void
store_int32(jit_int32_t base, eint32_t offset, eint32_t type)
{
    switch (type) {
	case t_int8:			case t_uint8:
	    jit_stxi_c(offset, base, JIT_V1);
	    break;
	case t_int16:			case t_uint16:
	    jit_stxi_s(offset, base, JIT_V1);
	    break;
	case t_int32:			case t_uint32:
	    jit_stxi_i(offset, base, JIT_V1);
	    break;
	case t_int:			case t_uint:
	    if (!(ejit_sync & ejit_sync_int_ext)) {
		zero_sign_extend(ejit_type);
		ejit_sync |= ejit_sync_int_ext;
	    }
	    store_ireg(base, offset);
	    break;
	case t_float32:
	    if (!(ejit_sync & ejit_sync_int_float)) {
		jit_extr_f(JIT_F1, JIT_V1);
		ejit_sync = (ejit_sync|ejit_sync_int_float) &
			    ~ejit_sync_long_float;
	    }
	    jit_stxi_f(offset, base, JIT_F1);
	    break;
	case t_float:
	    if (!(ejit_sync & ejit_sync_int_double)) {
		jit_extr_d(JIT_F0, JIT_V1);
		ejit_sync = (ejit_sync|ejit_sync_int_double) &
			    ~ejit_sync_long_double;
	    }
	    jit_stxi_d(offset, base, JIT_F0);
	    break;
	default:
	    abort();
    }
}

static void
store_int(jit_int32_t base, eint32_t offset, eint32_t type)
{
    ebool_t		dynamic = (ejit_sync & ejit_sync_dynamic) != 0;
    switch (type) {
	case t_int8:			case t_uint8:
	    jit_stxi_c(offset, base, JIT_V1);
	    break;
	case t_int16:			case t_uint16:
	    jit_stxi_s(offset, base, JIT_V1);
	    break;
	case t_int32:			case t_uint32:
	    jit_stxi_i(offset, base, JIT_V1);
	    break;
	case t_int:			case t_uint:
	    store_ireg(base, offset);
	    break;
	case t_float32:
#if __WORDSIZE == 64
	    if (dynamic)
		jit_extr_f(JIT_F1, JIT_V1);
	    else if (!(ejit_sync & ejit_sync_long_float)) {
		jit_extr_f(JIT_F1, JIT_V1);
		ejit_sync = (ejit_sync|ejit_sync_long_float) &
			    ~ejit_sync_int_float;
	    }
	    jit_stxi_f(offset, base, JIT_F1);
#else
	    if (dynamic)
		store_ireg_implicit(true);
	    else if (!(ejit_sync & ejit_sync_implicit)) {
		store_ireg_implicit(true);
		ejit_sync |= ejit_sync_implicit;
	    }
	    if (offset)
		jit_addi(base, base, offset);
	    jit_prepare(jit_call_default);
	    jit_pushargr(base);
	    jit_finishi(evm_store_float32);
	    if (!dynamic)
		ejit_sync &= ~(ejit_sync_float_ext|
			       ejit_sync_int_float|ejit_sync_int_double|
			       ejit_sync_long_float|ejit_sync_long_double);
#endif
	    break;
	case t_float:
#if __WORDSIZE == 64
	    if (dynamic)
		jit_extr_d(JIT_F0, JIT_V1);
	    else if (!(ejit_sync & ejit_sync_long_double)) {
		jit_extr_d(JIT_F0, JIT_V1);
		ejit_sync = (ejit_sync|ejit_sync_long_double) &
			    ~ejit_sync_int_float;
	    }
	    jit_stxi_d(offset, base, JIT_F0);
#else
	    if (dynamic)
		store_ireg_implicit(true);
	    else if (!(ejit_sync & ejit_sync_implicit)) {
		store_ireg_implicit(true);
		ejit_sync |= ejit_sync_implicit;
	    }
	    if (offset)
		jit_addi(base, base, offset);
	    jit_prepare(jit_call_default);
	    jit_pushargr(base);
	    jit_finishi(evm_store_float64);
	    if (!dynamic)
		ejit_sync &= ~(ejit_sync_float_ext|
			       ejit_sync_int_float|ejit_sync_int_double|
			       ejit_sync_long_float|ejit_sync_long_double);
#endif
	    break;
	default:
	    abort();
    }
}

static void
load_freg(jit_int32_t base, eint32_t offset)
{
    jit_ldxi_d(JIT_F0, base, offset);
}

static void
load_freg_reg_type(jit_int32_t base, eint32_t offset,
		   jit_int32_t dreg, jit_int32_t freg, eint32_t type)
{
    if (type == t_float32)
	jit_ldxi_f(freg, base, offset);
    else
	jit_ldxi_d(dreg, base, offset);
}

static void
load_freg_type(jit_int32_t base, eint32_t offset, eint32_t type)
{
    load_freg_reg_type(base, offset, JIT_F0, JIT_F1, type);
}

static void
load_freg_implicit(void)
{
    load_freg(JIT_V0, offsetof(ethread_t, r0) + offsetof(evalue_t, v.d));
}

static void
maybe_reload_freg_implicit(void)
{
    if (!(ejit_sync & ejit_sync_register)) {
	load_freg_implicit();
	if (!(ejit_sync & ejit_sync_dynamic))
	    ejit_sync |= ejit_sync_register;
    }
}

static void
store_freg(jit_int32_t base, eint32_t offset)
{
    jit_stxi_d(offset, base, JIT_F0);
}

static void
store_freg_value_type(jit_int32_t base, eint32_t offset)
{
    jit_movi(JIT_R0, t_float);
    jit_stxi_i(offset + offsetof(evalue_t, t), base, JIT_R0);
    store_freg(base, offset + offsetof(evalue_t, v.d));
}

static void
store_freg_implicit(ebool_t type)
{
    if (type)
	store_freg_value_type(JIT_V0, offsetof(ethread_t, r0));
    else
	store_freg(JIT_V0, offsetof(ethread_t, r0) + offsetof(evalue_t, v.d));
}

static void
store_float32(jit_int32_t base, eint32_t offset, eint32_t type)
{
    switch (type) {
	case t_int8:			case t_uint8:
	    if (!(ejit_sync & ejit_sync_int_float)) {
		jit_truncr_f_i(JIT_V1, JIT_F1);
		ejit_sync = (ejit_sync|ejit_sync_int_float) &
			    ~ejit_sync_long_float;
	    }
	    jit_stxi_c(offset, base, JIT_V1);
	    break;
	case t_int16:			case t_uint16:
	    if (!(ejit_sync & ejit_sync_int_float)) {
		jit_truncr_f_i(JIT_V1, JIT_F1);
		ejit_sync = (ejit_sync|ejit_sync_int_float) &
			    ~ejit_sync_long_float;
	    }
	    jit_stxi_s(offset, base, JIT_V1);
	    break;
	case t_int32:			case t_uint32:
	    if (!(ejit_sync & ejit_sync_int_float)) {
		jit_truncr_f_i(JIT_V1, JIT_F1);
		ejit_sync = (ejit_sync|ejit_sync_int_float) &
			    ~ejit_sync_long_float;
	    }
	    jit_stxi_i(offset, base, JIT_V1);
	    break;
	case t_int:		case t_uint:
#if __WORDSIZE == 64
	    if (!(ejit_sync & ejit_sync_long_float)) {
		jit_truncr_f_l(JIT_V1, JIT_F1);
		ejit_sync = (ejit_sync|ejit_sync_long_float) &
			    ~ejit_sync_int_float;
	    }
	    jit_stxi(offset, base, JIT_V1);
#else
	    if (!(ejit_sync & ejit_sync_float_ext))
		jit_extr_f_d(JIT_F0, JIT_F1);
	    store_freg_implicit(true);
	    if (offset)
		jit_addi(base, base, offset);
	    jit_prepare(jit_call_default);
	    jit_pushargr(base);
	    jit_finishi(evm_store_int64);
	    load_freg_implicit();
	    jit_extr_d_f(JIT_F1, JIT_F0);
#endif
	    break;
	case t_float32:
	    jit_stxi_f(offset, base, JIT_F1);
	    break;
	case t_float:
	    if (!(ejit_sync & ejit_sync_float_ext)) {
		jit_extr_f_d(JIT_F0, JIT_F1);
		ejit_sync |= ejit_sync_float_ext;
	    }
	    store_freg(base, offset);
	    break;
	default:
	    abort();
    }
}

static void
store_float(jit_int32_t base, eint32_t offset, eint32_t type)
{
    ebool_t		dynamic = (ejit_sync & ejit_sync_dynamic) != 0;

    switch (type) {
	case t_int8:			case t_uint8:
	    if (dynamic)
		jit_truncr_d_i(JIT_V1, JIT_F0);
	    else if (!(ejit_sync & ejit_sync_int_double)) {
		jit_truncr_d_i(JIT_V1, JIT_F0);
		ejit_sync = (ejit_sync|ejit_sync_int_double) &
			    ~ejit_sync_long_double;
	    }
	    jit_stxi_c(offset, base, JIT_V1);
	    break;
	case t_int16:			case t_uint16:
	    if (dynamic)
		jit_truncr_d_i(JIT_V1, JIT_F0);
	    else if (!(ejit_sync & ejit_sync_int_double)) {
		jit_truncr_d_i(JIT_V1, JIT_F0);
		ejit_sync = (ejit_sync|ejit_sync_int_double) &
			    ~ejit_sync_long_double;
	    }
	    jit_stxi_s(offset, base, JIT_V1);
	    break;
	case t_int32:			case t_uint32:
	    if (dynamic)
		jit_truncr_d_i(JIT_V1, JIT_F0);
	    else if (!(ejit_sync & ejit_sync_int_double)) {
		jit_truncr_d_i(JIT_V1, JIT_F0);
		ejit_sync = (ejit_sync|ejit_sync_int_double) &
			    ~ejit_sync_long_double;
	    }
	    jit_stxi_i(offset, base, JIT_V1);
	    break;
	case t_int:			case t_uint:
#if __WORDSIZE == 64
	    if (dynamic)
		jit_truncr_d_l(JIT_V1, JIT_F0);
	    else if (!(ejit_sync & ejit_sync_long_double)) {
		jit_truncr_d_l(JIT_V1, JIT_F0);
		ejit_sync = (ejit_sync|ejit_sync_long_double) &
			    ~ejit_sync_int_double;
	    }
	    jit_stxi_l(offset, base, JIT_V1);
#else
	    if (dynamic || !(ejit_sync & ejit_sync_implicit))
		store_freg_implicit(true);
	    if (offset)
		jit_addi(base, base, offset);
	    jit_prepare(jit_call_default);
	    jit_pushargr(base);
	    jit_finishi(evm_store_int64);
	    /* FIXME only required if float reg is non callee save */
	    load_freg_implicit();
#endif
	    break;
	case t_float32:
	    if (dynamic)
		jit_extr_d_f(JIT_F1, JIT_F0);
	    else if (!(ejit_sync & ejit_sync_float_ext)) {
		jit_extr_d_f(JIT_F1, JIT_F0);
		ejit_sync |= ejit_sync_float_ext;
	    }
	    jit_stxi_f(offset, base, JIT_F1);
	    break;
	case t_float:
	    store_freg(base, offset);
	    break;
	default:
	    abort();
    }
}

static void
store_dynamic(jit_int32_t base, eint32_t offset, eint32_t type)
{
    if (offset)
	jit_addi(base, base, offset);
    jit_prepare(jit_call_default);
    jit_pushargr(base);
    switch (type) {
	case t_int8:			case t_uint8:
	    jit_finishi(evm_store_int8);
	    break;
	case t_int16:			case t_uint16:
	    jit_finishi(evm_store_int16);
	    break;
	case t_int32:			case t_uint32:
	    jit_finishi(evm_store_int32);
	    break;
	case t_int:			case t_uint:
	    jit_finishi(evm_store_int64);
	    break;
	case t_float32:
	    jit_finishi(evm_store_float32);
	    break;
	case t_float:
	    jit_finishi(evm_store_float64);
	    break;
	default:
	    abort();
    }
}

/* reset state */
static void
emit_clear(void)
{
    ejit_sync = ejit_sync_dynamic|ejit_sync_implicit;
    ejit_type = t_void;
}

/* flush code before calling extern function that changes implicit */
static void
emit_flush(void)
{
    if (ejit_sync & ejit_sync_register) {
	switch (ejit_type) {
	    case t_int8:		case t_uint8:
	    case t_int16:		case t_uint16:
	    case t_int32:		case t_uint32:
		if (!(ejit_sync & ejit_sync_implicit)) {
		    if (!(ejit_sync & ejit_sync_int_ext))
			zero_sign_extend(ejit_type);
		    store_ireg_implicit(true);
		}
		break;
	    case t_int:
		if (!(ejit_sync & ejit_sync_implicit))
		    store_ireg_implicit(true);
		    break;
	    case t_uint:
		emit_sync_uint();
		break;
	    case t_float32:
		if (!(ejit_sync & ejit_sync_implicit)) {
		    if (!(ejit_sync & ejit_sync_float_ext))
			jit_extr_f_d(JIT_F0, JIT_F1);
		    store_freg_implicit(true);
		}
		break;
	    case t_float:
		if (!(ejit_sync & ejit_sync_implicit))
		    store_freg_implicit(true);
		    break;
	    default:
		    break;
	}
    }
    emit_clear();
}

/* sync code before calling extern function */
static void
emit_sync(void)
{
    if (ejit_sync & ejit_sync_register) {
	switch (ejit_type) {
	    case t_int8:		case t_uint8:
	    case t_int16:		case t_uint16:
	    case t_int32:		case t_uint32:
	    if (!(ejit_sync & ejit_sync_implicit)) {
		if (!(ejit_sync & ejit_sync_int_ext))
		    zero_sign_extend(ejit_type);
		store_ireg_implicit(true);
		ejit_sync |= ejit_sync_implicit|ejit_sync_int_ext;
	    }
	    break;
	    case t_int:
		if (!(ejit_sync & ejit_sync_implicit)) {
		    store_ireg_implicit(true);
		    ejit_sync |= ejit_sync_implicit;
		}
		break;
	    case t_uint:
		emit_sync_uint();
		ejit_sync = ejit_sync_dynamic|ejit_sync_implicit;
		break;
	    case t_float32:
		if (!(ejit_sync & ejit_sync_implicit)) {
		    if (!(ejit_sync & ejit_sync_float_ext))
			jit_extr_f_d(JIT_F0, JIT_F1);
		    store_freg_implicit(true);
		}
		ejit_sync = ejit_sync_implicit;
		break;
	    case t_float:
		if (!(ejit_sync & ejit_sync_implicit))
		    store_freg_implicit(true);
		ejit_sync = ejit_sync_implicit;
		break;
	    default:
		ejit_sync = ejit_sync_dynamic|ejit_sync_implicit;
		break;
	}
    }
}

static void
emit_sync_uint(void)
{
    jit_node_t		*node;

#if __WORDSIZE == 32
    node = jit_blti(JIT_V2, 0);
    if (!(ejit_sync & ejit_sync_implicit))
	store_ireg_implicit(true);
    jit_patch(node);
    store_ireg_implicit_rgz(0, JIT_V1, JIT_V2);
#else
    node = jit_blti(JIT_V1, 0);
    if (!(ejit_sync & ejit_sync_implicit))
	store_ireg_implicit(true);
    jit_patch(node);
    store_ireg_implicit_rgz(0, JIT_V1);
#endif
}

#define CODE		1
#  include "eemit_code.c"
#undef CODE

typedef enum emit_code {
    emit_code_eol	= -1,
    emit_code_label,
    emit_code_nil,
    emit_code_int,
    emit_code_float,
    emit_code_ll,
    emit_code_load,
    emit_code_load_jmp,
    emit_code_load_incdec,
    emit_code_load_incdec_store,
    emit_code_load_push,
    emit_code_load_push_cmp,
    emit_code_load_push_cmp_jmp,
    emit_code_load_push_addsub,
    emit_code_load_push_int,
    emit_code_load_push_int_cmp,
    emit_code_load_push_int_cmp_jmp,
    emit_code_load_push_int_addsub,
    emit_code_load_push_float,
    emit_code_load_push_float_cmp,
    emit_code_load_push_float_cmp_jmp,
    emit_code_load_push_load,
    emit_code_load_push_load_cmp,
    emit_code_load_push_load_cmp_jmp,
    emit_code_load_push_load_addsub,
    emit_code_lh,
    emit_code_lhref,
    emit_code_lv,
    emit_code_lvi,
    emit_code_lvref,
    emit_code_lr,
    emit_code_lrref,
    emit_code_lref,
    emit_code_pd,
    emit_code_pdt,
    emit_code_pb,
    emit_code_pbt,
    emit_code_pv,
    emit_code_pvi,
    emit_code_pr,
    emit_code_pp,
    emit_code_anon,
    emit_code_sd,
    emit_code_sdt,
    emit_code_sb,
    emit_code_sbt,
    emit_code_ss,
    emit_code_sst,
    emit_code_ssv,
    emit_code_sh,
    emit_code_shref,
    emit_code_sv,
    emit_code_svi,
    emit_code_svref,
    emit_code_sr,
    emit_code_srref,
    emit_code_sc,
    emit_code_sref,
    emit_code_jmp,
    emit_code_j,
    emit_code_jh,
    emit_code_enter,
    emit_code_begin,
    emit_code_off,
    emit_code_push,
    emit_code_push_cmp,
    emit_code_push_cmp_jmp,
    emit_code_push_addsub,
    emit_code_push_int,
    emit_code_push_int_cmp,
    emit_code_push_int_cmp_jmp,
    emit_code_push_int_addsub,
    emit_code_push_float,
    emit_code_push_float_cmp,
    emit_code_push_float_cmp_jmp,
    emit_code_push_load,
    emit_code_push_load_cmp,
    emit_code_push_load_cmp_jmp,
    emit_code_push_load_addsub,
    emit_code_pusht,
    emit_code_pushv,
    emit_code_pop,
    emit_code_call,
    emit_code_meth,
    emit_code_ecm,
    emit_code_blt,
    emit_code_ret,
    emit_code_rett,
    emit_code_reti,
    emit_code_retit,
    emit_code_cmp,
    emit_code_cmp_jmp,
    emit_code_and,
    emit_code_or,
    emit_code_xor,
    emit_code_mul2,
    emit_code_div2,
    emit_code_shl,
    emit_code_shr,
    emit_code_add,
    emit_code_sub,
    emit_code_mul,
    emit_code_div,
    emit_code_trunc2,
    emit_code_rem,
    emit_code_atan2,
    emit_code_pow,
    emit_code_hypot,
    emit_code_complex,
    emit_code_incdec,
    emit_code_incdec_store,
    emit_code_boolean,
    emit_code_com,
    emit_code_inv,
    emit_code_neg,
    emit_code_signbit,
    emit_code_signum,
    emit_code_rational,
    emit_code_integer_p,
    emit_code_rational_p,
    emit_code_float_p,
    emit_code_real_p,
    emit_code_complex_p,
    emit_code_number_p,
    emit_code_finite_p,
    emit_code_inf_p,
    emit_code_nan_p,
    emit_code_num,
    emit_code_den,
    emit_code_real,
    emit_code_imag,
    emit_code_arg,
    emit_code_conj,
    emit_code_floor,
    emit_code_trunc,
    emit_code_round,
    emit_code_ceil,
    emit_code_abs,
    emit_code_sqrt,
    emit_code_cbrt,
    emit_code_sin,
    emit_code_cos,
    emit_code_tan,
    emit_code_asin,
    emit_code_acos,
    emit_code_atan,
    emit_code_sinh,
    emit_code_cosh,
    emit_code_tanh,
    emit_code_asinh,
    emit_code_acosh,
    emit_code_atanh,
    emit_code_proj,
    emit_code_exp,
    emit_code_log,
    emit_code_log2,
    emit_code_log10,
    emit_code_new,
    emit_code_mv,
    emit_code_vnew,
    emit_code_vnewi,
    emit_code_vmv,
    emit_code_renew,
    emit_code_renewi,
    emit_code_fref,
    emit_code_apply,
    emit_code_aret,
    emit_code_szof,
    emit_code_szofva,
    emit_code_szofdim,
    emit_code_szofdimi,
    emit_code_tpof,
    emit_code_subtpof,
    emit_code_rankof,
    emit_code_vas,
    emit_code_val,
    emit_code_lva,
    emit_code_lvai,
    emit_code_sva,
    emit_code_svai,
    emit_code_pva,
    emit_code_try,
    emit_code_catch,
    emit_code_throw,
    emit_code_unwind,
    emit_code_exit,
    emit_code_setup,
    emit_code_intp_0,
    emit_code_intp_v,
    emit_code_intp_n1,
    emit_code_realp_v,
    emit_code_nump_0,
    emit_code_nump_v,
    emit_code_block,
} emit_code_t;

typedef struct emit_struct {
    east_list_t		*list;
    east_node_t		*note;
    east_list_t		*ast0;
    east_list_t		*ast1;
    east_list_t		*ast2;
    east_list_t		*ast3;
    east_list_t		*ast4;
} emit_struct_t;

static ebool_t
emit_first_list(emit_struct_t *info)
{
    east_list_t		*list;
    east_node_t		*node;
    east_node_t		*note;

    note = null;
    for (list = info->list; list; list = east_next(list)) {
	node = east_node(list);
	if (node->code != eop_note)
	    break;
	note = node;
    }
    if (note)
	note->cnote.note = jit_note(note);

    info->note = null;
    info->list = list;
    return (list != null);
}

static ebool_t
emit_next_list(emit_struct_t *info)
{
    east_list_t		*list;
    east_node_t		*node;

    for (list = east_next(info->list); list; list = east_next(list)) {
	node = east_node(list);
	if (node->code != eop_note)
	    break;
	info->note = node;
    }

    info->list = list;
    return (list != null);
}

static eint32_t
emit_jump_implicit_used_p(emit_struct_t *info, east_node_t *jump)
{
    eint32_t		 flags;
    elabel_t		*label;

    label = jump->clabel.label;
    if (ecfg_implicit_used_p(east_next(elabel_list(label))) ||
	ecfg_implicit_used_p(east_next(info->list)))
	flags = code_flags_implicit;
    else
	flags = 0;

    return (flags);
}

static emit_code_t
emit_dispatch_load_incdec(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_incdec;
    if (emit_next_list(info)) {
	info->ast2 = info->list;
	switch (east_node(info->ast2)->code) {
	    case eop_sd:	case eop_sdt:
	    case eop_sb:	case eop_sbt:	case eop_sc:
		if (ecfg_implicit_used_p(east_next(info->list)))
		    east_node(info->ast2)->csimple.flags |= code_flags_implicit;
		code = emit_code_load_incdec_store;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push_int_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push_int_cmp;
    if (emit_next_list(info)) {
	info->ast4 = info->list;
	switch (east_node(info->ast4)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast4)))
		    return (emit_code_load_push_int_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push_int(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push_int;
    if (emit_next_list(info)) {
	info->ast3 = info->list;
	switch (east_node(info->ast3)->code) {
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_load_push_int_cmp(info);
		break;
	    case eop_add:	case eop_sub:
		code = emit_code_load_push_int_addsub;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push_float_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push_float_cmp;
    if (emit_next_list(info)) {
	info->ast4 = info->list;
	switch (east_node(info->ast4)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast4)))
		    return (emit_code_load_push_float_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push_float(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push_float;
    if (emit_next_list(info)) {
	info->ast3 = info->list;
	switch (east_node(info->ast3)->code) {
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_load_push_float_cmp(info);
		break;
#if 0
	    case eop_add:	case eop_sub:
		code = emit_code_load_push_float_addsub;
#endif
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push_load_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push_load_cmp;
    if (emit_next_list(info)) {
	info->ast4 = info->list;
	switch (east_node(info->ast4)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast4)))
		    return (emit_code_load_push_load_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push_load(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push_load;
    if (emit_next_list(info)) {
	info->ast3 = info->list;
	switch (east_node(info->ast3)->code) {
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_load_push_load_cmp(info);
		break;
	    case eop_add:	case eop_sub:
		code = emit_code_load_push_load_addsub;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push_cmp;
    if (emit_next_list(info)) {
	info->ast3 = info->list;
	switch (east_node(info->ast3)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast3)))
		    return (emit_code_load_push_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load_push(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load_push;
    if (emit_next_list(info)) {
	info->ast2 = info->list;
	switch (east_node(info->ast2)->code) {
	    case eop_int:	case eop_long:
		code = emit_dispatch_load_push_int(info);
		break;
	    case eop_float:
		code = emit_dispatch_load_push_float(info);
		break;
	    case eop_ld:	case eop_ldt:	case eop_lb:	case eop_lbt:
	    case eop_lc:
		code = emit_dispatch_load_push_load(info);
		break;
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_load_push_cmp(info);
		break;
	    case eop_add:	case eop_sub:
		code = emit_code_load_push_addsub;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_load(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_load;
    if (emit_next_list(info)) {
	info->ast1 = info->list;
	switch (east_node(info->ast1)->code) {
	    case eop_inc:	case eop_dec:
		code = emit_dispatch_load_incdec(info);
		break;
	    case eop_push:
		code = emit_dispatch_load_push(info);
		if (code == emit_code_load_push &&
		    ecfg_implicit_used_p(east_next(info->list)))
		    east_node(info->ast1)->csimple.flags |= code_flags_implicit;
		break;
	    case eop_jt:	case eop_jf:
		east_node(info->ast1)->csimple.flags |=
		    emit_jump_implicit_used_p(info, east_node(info->ast1));
		code = emit_code_load_jmp;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push_int_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    if (emit_next_list(info)) {
	info->ast3 = info->list;
	switch (east_node(info->ast3)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast3)))
		    return (emit_code_push_int_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		code = emit_code_push_int_cmp;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push_int(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_push_int;
    if (emit_next_list(info)) {
	info->ast2 = info->list;
	switch (east_node(info->ast2)->code) {
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_push_int_cmp(info);
		break;
	    case eop_add:	case eop_sub:
		code = emit_code_push_int_addsub;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push_float_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    if (emit_next_list(info)) {
	info->ast3 = info->list;
	switch (east_node(info->ast3)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast3)))
		    return (emit_code_push_float_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		code = emit_code_push_float_cmp;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push_float(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_push_float;
    if (emit_next_list(info)) {
	info->ast2 = info->list;
	switch (east_node(info->ast2)->code) {
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_push_float_cmp(info);
		break;
#if 0
	    case eop_add:	case eop_sub:
		code = emit_code_push_float_addsub;
#endif
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push_load_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_push_load_cmp;
    if (emit_next_list(info)) {
	info->ast3 = info->list;
	switch (east_node(info->ast3)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast3)))
		    return (emit_code_push_load_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push_load(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_push_load;
    if (emit_next_list(info)) {
	info->ast2 = info->list;
	switch (east_node(info->ast2)->code) {
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_push_load_cmp(info);
		break;
	    case eop_add:	case eop_sub:
		code = emit_code_push_load_addsub;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push_cmp(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_push_cmp;
    if (emit_next_list(info)) {
	info->ast2 = info->list;
	switch (east_node(info->ast2)->code) {
	    case eop_jt:	case eop_jf:
		if (!emit_jump_implicit_used_p(info, east_node(info->ast2)))
		    return (emit_code_push_cmp_jmp);
		/* FALLTHROUGH */
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_push(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_push;
    if (emit_next_list(info)) {
	info->ast1 = info->list;
	switch (east_node(info->ast1)->code) {
	    case eop_int:	case eop_long:
		code = emit_dispatch_push_int(info);
		break;
	    case eop_float:
		code = emit_dispatch_push_float(info);
		break;
	    case eop_ld:	case eop_ldt:	case eop_lb:	case eop_lbt:
	    case eop_lc:
		code = emit_dispatch_push_load(info);
		break;
	    case eop_ne:	case eop_lt:	case eop_le:
	    case eop_eq:	case eop_ge:	case eop_gt:
		code = emit_dispatch_push_cmp(info);
		break;
	    case eop_add:	case eop_sub:
		code = emit_code_push_addsub;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_cmp(emit_struct_t *info)
{
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    if (emit_next_list(info)) {
	info->ast1 = info->list;
	if (east_node(info->ast1)->code == eop_jt ||
	    east_node(info->ast1)->code == eop_jf) {
	    if (!emit_jump_implicit_used_p(info, east_node(info->ast1)))
		return (emit_code_cmp_jmp);
	}
	info->list = list;
	info->note = note;
    }

    return (emit_code_cmp);
}

static emit_code_t
emit_dispatch_incdec(emit_struct_t *info)
{
    emit_code_t		 code;
    east_list_t		*list;
    east_node_t		*note;

    list = info->list;
    note = info->note;
    code = emit_code_incdec;
    if (emit_next_list(info)) {
	info->ast1 = info->list;
	switch (east_node(info->ast1)->code) {
	    case eop_sd:	case eop_sdt:
	    case eop_sb:	case eop_sbt:	case eop_sc:
		if (ecfg_implicit_used_p(east_next(info->list)))
		    east_node(info->ast1)->csimple.flags |= code_flags_implicit;
		code = emit_code_incdec_store;
		break;
	    default:
		info->list = list;
		info->note = note;
		break;
	}
    }

    return (code);
}

static emit_code_t
emit_dispatch_jmp(emit_struct_t *info)
{
    east_node(info->ast0)->csimple.flags |=
	emit_jump_implicit_used_p(info, east_node(info->ast0));

    return (emit_code_jmp);
}

static emit_code_t
emit_dispatch(emit_struct_t *info)
{
    emit_code_t		 code;

    if (!emit_first_list(info))
	return (emit_code_eol);

    info->ast0 = info->list;
    switch (east_node(info->ast0)->code) {
	case eop_label:		code = emit_code_label;		break;
	case eop_nil:		code = emit_code_nil;		break;
	case eop_int:	case eop_long:
	    code = emit_code_int;				break;
	case eop_float:		code = emit_code_float;		break;
	case eop_ll:		code = emit_code_ll;		break;
	case eop_ld:	case eop_ldt:	case eop_lb:
	case eop_lbt:		code = emit_dispatch_load(info);break;
	case eop_ls:		code = emit_code_load;		break;
	case eop_lh:		code = emit_code_lh;		break;
	case eop_lhref:		code = emit_code_lhref;		break;
	case eop_lv:		code = emit_code_lv;		break;
	case eop_lvi:		code = emit_code_lvi;		break;
	case eop_lvref:		code = emit_code_lvref;		break;
	case eop_lr:		code = emit_code_lr;		break;
	case eop_lrref:		code = emit_code_lrref;		break;
	case eop_lc:		code = emit_dispatch_load(info);break;
	case eop_lref:		code = emit_code_lref;		break;
	case eop_pd:		code = emit_code_pd;		break;
	case eop_pdt:		code = emit_code_pdt;		break;
	case eop_pb:		code = emit_code_pb;		break;
	case eop_pbt:		code = emit_code_pbt;		break;
	case eop_pv:		code = emit_code_pv;		break;
	case eop_pvi:		code = emit_code_pvi;		break;
	case eop_pr:		code = emit_code_pr;		break;
	case eop_pp:		code = emit_code_pp;		break;
	case eop_anon:		code = emit_code_anon;		break;
	case eop_sd:		code = emit_code_sd;		break;
	case eop_sdt:		code = emit_code_sdt;		break;
	case eop_sb:		code = emit_code_sb;		break;
	case eop_sbt:		code = emit_code_sbt;		break;
	case eop_ss:		code = emit_code_ss;		break;
	case eop_sst:		code = emit_code_sst;		break;
	case eop_ssv:		code = emit_code_ssv;		break;
	case eop_sh:		code = emit_code_sh;		break;
	case eop_shref:		code = emit_code_shref;		break;
	case eop_sv:		code = emit_code_sv;		break;
	case eop_svi:		code = emit_code_svi;		break;
	case eop_svref:		code = emit_code_svref;		break;
	case eop_sr:		code = emit_code_sr;		break;
	case eop_srref:		code = emit_code_srref;		break;
	case eop_sc:		code = emit_code_sc;		break;
	case eop_sref:		code = emit_code_sref;		break;
	case eop_jt:	case eop_jf:
	    code = emit_dispatch_jmp(info);			break;
	case eop_j:		code = emit_code_j;		break;
	case eop_jh:		code = emit_code_jh;		break;
	case eop_enter:		code = emit_code_enter;		break;
	case eop_begin:		code = emit_code_begin;		break;
	case eop_off:		code = emit_code_off;		break;
	case eop_push:		code = emit_dispatch_push(info);break;
	case eop_pusht:		code = emit_code_pusht;		break;
	case eop_pushv:		code = emit_code_pushv;		break;
	case eop_pop:		code = emit_code_pop;		break;
	case eop_call:		code = emit_code_call;		break;
	case eop_meth:		code = emit_code_meth;		break;
	case eop_ecm:		code = emit_code_ecm;		break;
	case eop_blt:		code = emit_code_blt;		break;
	case eop_ret:		code = emit_code_ret;		break;
	case eop_rett:		code = emit_code_rett;		break;
	case eop_reti:		code = emit_code_reti;		break;
	case eop_retit:		code = emit_code_retit;		break;
	case eop_ne:	case eop_lt:	case eop_le:
	case eop_eq:	case eop_ge:	case eop_gt:
	    code = emit_dispatch_cmp(info);			break;
	case eop_and:		code = emit_code_and;		break;
	case eop_or:		code = emit_code_or;		break;
	case eop_xor:		code = emit_code_xor;		break;
	case eop_mul2:		code = emit_code_mul2;		break;
	case eop_div2:		code = emit_code_div2;		break;
	case eop_shl:		code = emit_code_shl;		break;
	case eop_shr:		code = emit_code_shr;		break;
	case eop_add:		code = emit_code_add;		break;
	case eop_sub:		code = emit_code_sub;		break;
	case eop_mul:		code = emit_code_mul;		break;
	case eop_div:		code = emit_code_div;		break;
	case eop_trunc2:	code = emit_code_trunc2;	break;
	case eop_rem:		code = emit_code_rem;		break;
	case eop_atan2:		code = emit_code_atan2;		break;
	case eop_pow:		code = emit_code_pow;		break;
	case eop_hypot:		code = emit_code_hypot;		break;
	case eop_complex:	code = emit_code_complex;	break;
	case eop_inc:	case eop_dec:
	    code = emit_dispatch_incdec(info);			break;
	case eop_bool:	case eop_not:
	    code = emit_code_boolean;				break;
	case eop_com:		code = emit_code_com;		break;
	case eop_inv:		code = emit_code_inv;		break;
	case eop_neg:		code = emit_code_neg;		break;
	case eop_signbit:	code = emit_code_signbit;	break;
	case eop_signum:	code = emit_code_signum;	break;
	case eop_rational:	code = emit_code_rational;	break;
	case eop_integer_p:	code = emit_code_integer_p;	break;
	case eop_rational_p:	code = emit_code_rational_p;	break;
	case eop_float_p:	code = emit_code_float_p;	break;
	case eop_real_p:	code = emit_code_real_p;	break;
	case eop_complex_p:	code = emit_code_complex_p;	break;
	case eop_number_p:	code = emit_code_number_p;	break;
	case eop_finite_p:	code = emit_code_finite_p;	break;
	case eop_inf_p:		code = emit_code_inf_p;		break;
	case eop_nan_p:		code = emit_code_nan_p;		break;
	case eop_num:		code = emit_code_num;		break;
	case eop_den:		code = emit_code_den;		break;
	case eop_real:		code = emit_code_real;		break;
	case eop_imag:		code = emit_code_imag;		break;
	case eop_arg:		code = emit_code_arg;		break;
	case eop_conj:		code = emit_code_conj;		break;
	case eop_floor:		code = emit_code_floor;		break;
	case eop_trunc:		code = emit_code_trunc;		break;
	case eop_round:		code = emit_code_round;		break;
	case eop_ceil:		code = emit_code_ceil;		break;
	case eop_abs:		code = emit_code_abs;		break;
	case eop_sqrt:		code = emit_code_sqrt;		break;
	case eop_cbrt:		code = emit_code_cbrt;		break;
	case eop_sin:		code = emit_code_sin;		break;
	case eop_cos:		code = emit_code_cos;		break;
	case eop_tan:		code = emit_code_tan;		break;
	case eop_asin:		code = emit_code_asin;		break;
	case eop_acos:		code = emit_code_acos;		break;
	case eop_atan:		code = emit_code_atan;		break;
	case eop_sinh:		code = emit_code_sinh;		break;
	case eop_cosh:		code = emit_code_cosh;		break;
	case eop_tanh:		code = emit_code_tanh;		break;
	case eop_asinh:		code = emit_code_asinh;		break;
	case eop_acosh:		code = emit_code_acosh;		break;
	case eop_atanh:		code = emit_code_atanh;		break;
	case eop_proj:		code = emit_code_proj;		break;
	case eop_exp:		code = emit_code_exp;		break;
	case eop_log:		code = emit_code_log;		break;
	case eop_log2:		code = emit_code_log2;		break;
	case eop_log10:		code = emit_code_log10;		break;
	case eop_new:		code = emit_code_new;		break;
	case eop_mv:		code = emit_code_mv;		break;
	case eop_vnew:		code = emit_code_vnew;		break;
	case eop_vnewi:		code = emit_code_vnewi;		break;
	case eop_vmv:		code = emit_code_vmv;		break;
	case eop_renew:		code = emit_code_renew;		break;
	case eop_renewi:	code = emit_code_renewi;	break;
	case eop_fref:		code = emit_code_fref;		break;
	case eop_apply:		code = emit_code_apply;		break;
	case eop_aret:		code = emit_code_aret;		break;
	case eop_szof:		code = emit_code_szof;		break;
	case eop_szofva:	code = emit_code_szofva;	break;
	case eop_szofdim:	code = emit_code_szofdim;	break;
	case eop_szofdimi:	code = emit_code_szofdimi;	break;
	case eop_tpof:		code = emit_code_tpof;		break;
	case eop_subtpof:	code = emit_code_subtpof;	break;
	case eop_rankof:	code = emit_code_rankof;	break;
	case eop_vas:		code = emit_code_vas;		break;
	case eop_val:		code = emit_code_val;		break;
	case eop_lva:		code = emit_code_lva;		break;
	case eop_lvai:		code = emit_code_lvai;		break;
	case eop_sva:		code = emit_code_sva;		break;
	case eop_svai:		code = emit_code_svai;		break;
	case eop_pva:		code = emit_code_pva;		break;
	case eop_try:		code = emit_code_try;		break;
	case eop_catch:		code = emit_code_catch;		break;
	case eop_throw:		code = emit_code_throw;		break;
	case eop_unwind:	code = emit_code_unwind;	break;
	case eop_exit:		code = emit_code_exit;		break;
	case eop_setup:		code = emit_code_setup;		break;
	case eop_intp_0:	code = emit_code_intp_0;	break;
	case eop_intp_v:	code = emit_code_intp_v;	break;
	case eop_intp_n1:	code = emit_code_intp_n1;	break;
	case eop_realp_v:	code = emit_code_realp_v;	break;
	case eop_nump_0:	code = emit_code_nump_0;	break;
	case eop_nump_v:	code = emit_code_nump_v;	break;
	case eop_block:		code = emit_code_block;		break;
	default:		abort();
    }
    info->list = east_next(info->list);

    return (code);
}

void
eemit(efunction_t *function)
{
#if !SIMPLE_JIT_DEBUG
    ecdd_t		 cdd;
    emit_code_t		 code;
    edata_t		 data;
    emit_struct_t	 emit;
    eint32_t		 type;
    elabel_t		*clabel;
    jit_node_t		*jinstr;
    eint32_t		 offset;
    eint32_t		 table_offset;

    /* reset function scope information */
    table_offset = table_vector->offset;
    label_vector->offset = patch_vector->offset = 0;

    jinstr = jit_label();
    jinstr->flag |= jit_flag_use;
    function->prolog = jinstr;
    ejit_sync = ejit_sync_implicit;

    emit.list = function->code;
    while ((code = emit_dispatch(&emit)) != emit_code_eol) {
	switch (code) {
	    case emit_code_label:
		/* call emit_flush and not emit_clear as there may
		 * be code that loads an implicit value just before a label,
		 * what is a common expansion of the ternary operator
		 * FIXME this should not be a requirement and should
		 * just check if implicit value is used */
		emit_flush();
		clabel = east_node(emit.ast0)->clabel.label;
		if (clabel->value) {
		    /* if label already referenced */
		    offset = eget_fixnum(clabel->value);
		    /* ensure forward label is actually undefined */
		    assert(offset < 0);
		    offset = -offset;
		    clabel->value = efixnum(offset);
		    labels[offset] = jit_label();
		}
		else {
		    /* remember label address is known */
		    if (label_vector->offset >= label_vector->length) {
			erenew_vector(label_vector, label_vector->length + 32);
			labels = label_vector->v.obj;
		    }
		    clabel->value = efixnum(label_vector->offset);
		    labels[label_vector->offset++] = jit_label();
		}
		break;
	    case emit_code_nil:
		emit_clear();
		jit_movi(JIT_R0, t_void);
		jit_stxi_i(offsetof(ethread_t, r0) + offsetof(evalue_t, t),
			   JIT_V0, JIT_R0);
		jit_stxi(offsetof(ethread_t, r0) + offsetof(evalue_t, v.o),
			 JIT_V0, JIT_R0);
		break;
	    case emit_code_int:
		emit_int(emit.ast0);
		break;
	    case emit_code_float:
		emit_float(emit.ast0);
		break;
	    case emit_code_ll:
		emit_clear();
		/* constants are not relocated */
		data.P = els[immediate_offset(east_node(emit.ast0))];
		type = eobject_type(data.P);
		jit_movi(JIT_R0, type);
		jit_stxi_i(offsetof(ethread_t, r0) + offsetof(evalue_t, t),
			   JIT_V0, JIT_R0);
		if (type != t_cdd) {
		    jit_movi(JIT_R0, (jit_word_t)data.P);
		    jit_stxi(offsetof(ethread_t, r0) +
			     offsetof(evalue_t, v.o), JIT_V0, JIT_R0);
		}
		else {
		    cdd = *(ecdd_t *)data.P;
		    data.F = real(cdd);
		    jit_movi_d(JIT_F0, data.F);
		    jit_stxi_d(offsetof(ethread_t, r0) +
			       offsetof(evalue_t, v.d), JIT_V0, JIT_F0);
		    if (data.F != imag(cdd)) {
			data.F = imag(cdd);
			jit_movi_d(JIT_F0, data.F);
		    }
		    jit_stxi_d(offsetof(ethread_t, r0) +
			       offsetof(evalue_t, v.d) + sizeof(double),
			       JIT_V0, JIT_F0);
		}
		ejit_type = emit.ast0->itype;
		break;
	    case emit_code_load:
		emit_load(emit.ast0);
		break;
	    case emit_code_load_jmp:
		emit_load_jmp(emit.ast0, emit.ast1);
		break;
	    case emit_code_load_incdec:
		emit_load_incdec(emit.ast0, emit.ast1);
		break;
	    case emit_code_load_incdec_store:
		emit_load_incdec_store(emit.ast0, emit.ast1, emit.ast2);
		break;
	    case emit_code_load_push:
		emit_load_push(emit.ast0, emit.ast1);
		break;
	    case emit_code_load_push_cmp:
		emit_load_push_load_cmp(emit.ast0, emit.ast1,
					null, emit.ast2);
		break;
	    case emit_code_load_push_cmp_jmp:
		emit_load_push_load_cmp_jmp(emit.ast0, emit.ast1, null,
					    emit.ast2, emit.ast3);
		break;
	    case emit_code_load_push_addsub:
		emit_load_push_load_addsub(emit.ast0, emit.ast1,
					   null, emit.ast2);
		break;
	    case emit_code_load_push_int:
		emit_load_push(emit.ast0, emit.ast1);
		emit_int(emit.ast2);
		break;
	    case emit_code_load_push_int_cmp:
		emit_load_push_int_cmp(emit.ast0, emit.ast1,
				       emit.ast2, emit.ast3);
		break;
	    case emit_code_load_push_int_cmp_jmp:
		emit_load_push_int_cmp_jmp(emit.ast0, emit.ast1, emit.ast2,
					   emit.ast3, emit.ast4);
		break;
	    case emit_code_load_push_int_addsub:
		emit_load_push_int_addsub(emit.ast0, emit.ast1,
					  emit.ast2, emit.ast3);
		break;
	    case emit_code_load_push_float:
		emit_load_push(emit.ast0, emit.ast1);
		emit_float(emit.ast2);
		break;
	    case emit_code_load_push_float_cmp:
		emit_load_push_float_cmp(emit.ast0, emit.ast1,
					 emit.ast2, emit.ast3);
		break;
	    case emit_code_load_push_float_cmp_jmp:
		emit_load_push_float_cmp_jmp(emit.ast0, emit.ast1, emit.ast2,
					     emit.ast3, emit.ast4);
		break;
	    case emit_code_load_push_load:
		emit_load_push(emit.ast0, emit.ast1);
		emit_load(emit.ast2);
		break;
	    case emit_code_load_push_load_cmp:
		emit_load_push_load_cmp(emit.ast0, emit.ast1,
					emit.ast2, emit.ast3);
		break;
	    case emit_code_load_push_load_cmp_jmp:
		emit_load_push_load_cmp_jmp(emit.ast0, emit.ast1, emit.ast2,
					    emit.ast3, emit.ast4);
		break;
	    case emit_code_load_push_load_addsub:
		emit_load_push_load_addsub(emit.ast0, emit.ast1,
					   emit.ast2, emit.ast3);
		break;
	    case emit_code_lh:
		emit_flush();
		call_vv(evm_lh);
		break;
	    case emit_code_lhref:
		emit_flush();
		call_vi(evm_lhref, east_node(emit.ast0)->chash.arg);
		break;
	    case emit_code_lv:
		emit_flush();
		call_vi(evm_lv, east_node(emit.ast0)->cvector.rank);
		break;
	    case emit_code_lvi:
		emit_flush();
		call_vi(evm_lvi, east_node(emit.ast0)->cvector.offset);
		break;
	    case emit_code_lvref:
		emit_flush();
		call_vii(evm_lvref, east_node(emit.ast0)->cvector.rank,
			 east_node(emit.ast0)->cvector.arg);
		break;
	    case emit_code_lr:
		emit_lr(emit.ast0);
		break;
	    case emit_code_lrref:
		emit_flush();
		call_viiii(evm_lrref,
			   east_node(emit.ast0)->crecord.record->type,
			   east_node(emit.ast0)->crecord.symbol->offset,
			   east_node(emit.ast0)->crecord.symbol->type,
			   east_node(emit.ast0)->crecord.arg);
		break;
	    case emit_code_lref:
		emit_flush();
		call_vv(evm_lref);
		break;
	    case emit_code_pd:
		emit_clear();
		/* r0.t = t_globalref */
		jit_movi(JIT_R0, t_globalref);
		jit_stxi_i(offsetof(ethread_t, r0) * offsetof(evalue_t, t),
			   JIT_V0, JIT_R0);
		/* r0.v.o = eds + atom->offset */
		jit_movi(JIT_R0, (jit_word_t)
			 ((eint8_t *)eds +
			  east_node(emit.ast0)->csymbol.symbol->offset *
			  sizeof(eobject_t)));
		jit_stxi(offsetof(ethread_t, r0) + offsetof(evalue_t, v.o),
			 JIT_V0, JIT_R0);
		break;
	    case emit_code_pdt:
		emit_clear();
		call_vi(evm_pdt, east_node(emit.ast0)->csymbol.symbol->offset);
		break;
	    case emit_code_pb:
		emit_clear();
		emit_stackref(east_node(emit.ast0)->csymbol.symbol->offset,
			      t_stackref);
		break;
	    case emit_code_pbt:
		emit_clear();
		emit_stackref(east_node(emit.ast0)->csymbol.symbol->offset,
			      t_t_stackref);
		break;
	    case emit_code_pv:
		emit_flush();
		call_vi(evm_pv, east_node(emit.ast0)->cvector.rank);
		break;
	    case emit_code_pvi:
		emit_flush();
		call_vi(evm_pvi, east_node(emit.ast0)->cvector.offset);
		break;
	    case emit_code_pr:
		emit_flush();
		call_viii(evm_pr,
			  east_node(emit.ast0)->crecord.record->type,
			  east_node(emit.ast0)->crecord.symbol->offset,
			  east_node(emit.ast0)->crecord.symbol->type);
		break;
	    case emit_code_pp:
		emit_flush();
		call_vv(evm_pp);
		break;
	    case emit_code_anon:
		emit_flush();
		call_vii(evm_anon,
			 east_node(emit.ast0)->crecord.record->type,
			 east_node(emit.ast0)->crecord.record->type ?
			 east_node(emit.ast0)->crecord.record->size :
			 sizeof(eobject_t));
		break;
	    case emit_code_sd:
		emit_sync();
		call_vi(evm_sd, east_node(emit.ast0)->csymbol.symbol->offset);
		break;
	    case emit_code_sdt:
		emit_sdt(emit.ast0);
		break;
	    case emit_code_sb:
		emit_sb(emit.ast0);
		break;
	    case emit_code_sbt:
		emit_sbt(emit.ast0);
		break;
	    case emit_code_ss:
		emit_ss(emit.ast0);
		break;
	    case emit_code_sst:
		emit_sst(emit.ast0);
		break;
	    case emit_code_ssv:
		emit_ss(emit.ast0);
		break;
	    case emit_code_sh:
		emit_sync();
		call_vv(evm_sh);
		break;
	    case emit_code_shref:
		emit_sync();
		call_vi(evm_shref, east_node(emit.ast0)->chash.arg);
		break;
	    case emit_code_sv:
		emit_sync();
		call_vi(evm_sv, east_node(emit.ast0)->cvector.rank);
		break;
	    case emit_code_svi:
		emit_sync();
		call_vi(evm_svi, east_node(emit.ast0)->cvector.offset);
		break;
	    case emit_code_svref:
		emit_sync();
		call_vi(evm_svref, east_node(emit.ast0)->cvector.arg);
		break;
	    case emit_code_sr:
		emit_sr(emit.ast0);
		break;
	    case emit_code_srref:
		emit_sync();
		call_vi(evm_srref, east_node(emit.ast0)->crecord.arg);
		break;
	    case emit_code_sc:
		emit_sc(emit.ast0);
		break;
	    case emit_code_sref:
		emit_sync();
		call_vv(evm_sref);
		break;
	    case emit_code_jmp:
		emit_load_jmp(null, emit.ast0);
		break;
	    case emit_code_j:
		emit_branch(east_node(emit.ast0)->clabel.label);
		break;
	    case emit_code_jh:
		emit_flush();
		emit_table_branch(els[immediate_offset(east_node(emit.ast0))],
				  immediate_offset(east_node(emit.ast0)));
		break;
	    case emit_code_enter:
		emit_flush();
		emit_enter(east_node(emit.ast0));
		break;
	    case emit_code_begin:
		emit_flush();
		call_vi(evm_begin, east_node(emit.ast0)->cstack.length);
		break;
	    case emit_code_off:
		emit_sync();
		call_vi(evm_off, east_node(emit.ast0)->cvector.rank);
		break;
	    case emit_code_push:
		emit_push(emit.ast0);
		break;
	    case emit_code_push_cmp:
		emit_load_push_load_cmp(null, emit.ast0,
					null, emit.ast1);
		break;
	    case emit_code_push_cmp_jmp:
		emit_load_push_load_cmp_jmp(null, emit.ast0, null,
					    emit.ast1, emit.ast2);
		break;
	    case emit_code_push_addsub:
		emit_load_push_load_addsub(null, emit.ast0,
					   null, emit.ast1);
		break;
	    case emit_code_push_int:
		emit_push(emit.ast0);
		emit_int(emit.ast1);
		break;
	    case emit_code_push_int_cmp:
		emit_load_push_int_cmp(null, emit.ast0,
				       emit.ast1, emit.ast2);
		break;
	    case emit_code_push_int_cmp_jmp:
		emit_load_push_int_cmp_jmp(null, emit.ast0, emit.ast1,
					   emit.ast2, emit.ast3);
		break;
	    case emit_code_push_int_addsub:
		emit_load_push_int_addsub(null, emit.ast0,
					  emit.ast1, emit.ast2);
		break;
	    case emit_code_push_float:
		emit_push(emit.ast0);
		emit_float(emit.ast1);
		break;
	    case emit_code_push_float_cmp:
		emit_load_push_float_cmp(null, emit.ast0,
					 emit.ast1, emit.ast2);
		break;
	    case emit_code_push_float_cmp_jmp:
		emit_load_push_float_cmp_jmp(null, emit.ast0, emit.ast1,
					     emit.ast2, emit.ast3);
		break;
	    case emit_code_push_load:
		emit_push(emit.ast0);
		emit_load(emit.ast1);
		break;
	    case emit_code_push_load_cmp:
		emit_load_push_load_cmp(null, emit.ast0,
					emit.ast1, emit.ast2);
		break;
	    case emit_code_push_load_cmp_jmp:
		emit_load_push_load_cmp_jmp(null, emit.ast0, emit.ast1,
					    emit.ast2, emit.ast3);
		break;
	    case emit_code_push_load_addsub:
		emit_load_push_load_addsub(null, emit.ast0,
					   emit.ast1, emit.ast2);
		break;
	    case emit_code_pusht:
		emit_pusht(emit.ast0);
		break;
	    case emit_code_pushv:
		switch (emit.ast0->itype) {
		    case t_int8:	case t_uint8:
		    case t_int16:	case t_uint16:
		    case t_int32:	case t_uint32:
		    case t_int:
		    case t_float32:	case t_float:
			emit_push(emit.ast0);
			break;
		    default:
			emit_sync();
			call_vv(evm_pushv);
			break;
		}
		break;
	    case emit_code_pop:
		emit_clear();
		call_vv(evm_pop);
		break;
	    case emit_code_call:
		emit_clear();
		emit_call(east_node(emit.ast0)->capply.offset);
		/* FIXME check return type */
		break;
	    case emit_code_meth:
		emit_flush();
		emit_method_call(east_node(emit.ast0)->capply.offset,
				 east_node(emit.ast0)->capply.type);
		break;
	    case emit_code_ecm:
		emit_flush();
		emit_explicit_call(east_node(emit.ast0)->capply.offset,
				   east_node(emit.ast0)->capply.type);
		break;
	    case emit_code_blt:
		emit_clear();
		call_vii(evm_blt, east_node(emit.ast0)->capply.offset,
			 east_node(emit.ast0)->capply.length);
		/* builtins (currently) have untyped return value */
		break;
	    case emit_code_ret:
		emit_flush();
		call_pi(evm_ret, 1);
		jit_retval(JIT_R0);
		jit_jmpr(JIT_R0);
		break;
	    case emit_code_rett:
		emit_flush();
		call_pii(evm_rett, 1, east_node(emit.ast0)->creturn.type);
		jit_retval(JIT_R0);
		jit_jmpr(JIT_R0);
		break;
	    case emit_code_reti:
		emit_flush();
		call_pi(evm_ret, east_node(emit.ast0)->creturn.length);
		jit_retval(JIT_R0);
		jit_jmpr(JIT_R0);
		break;
	    case emit_code_retit:
		emit_flush();
		call_pii(evm_rett, east_node(emit.ast0)->creturn.length,
			 east_node(emit.ast0)->creturn.type);
		jit_retval(JIT_R0);
		jit_jmpr(JIT_R0);
		break;
	    case emit_code_cmp:
		emit_cmp(emit.ast0);
		break;
	    case emit_code_cmp_jmp:
		emit_cmp_jmp(emit.ast0, emit.ast1);
		break;
	    case emit_code_and:
	    case emit_code_or:
	    case emit_code_xor:
		emit_andorxor(emit.ast0);
		break;
	    case emit_code_mul2:
		emit_flush();
		call_vv(evm_mul2);
		break;
	    case emit_code_div2:
		emit_flush();
		call_vv(evm_div2);
		break;
	    case emit_code_shl:
		emit_flush();
		call_vv(evm_shl);
		break;
	    case emit_code_shr:
		emit_flush();
		call_vv(evm_shr);
		break;
	    case emit_code_add:
	    case emit_code_sub:
		emit_addsub(emit.ast0);
		break;
	    case emit_code_mul:
		emit_flush();
		call_vv(evm_mul);
		break;
	    case emit_code_div:
		emit_flush();
		call_vv(evm_div);
		break;
	    case emit_code_trunc2:
		emit_flush();
		call_vv(evm_trunc2);
		break;
	    case emit_code_rem:
		emit_flush();
		call_vv(evm_rem);
		break;
	    case emit_code_atan2:
		emit_flush();
		call_vv(evm_atan2);
		break;
	    case emit_code_pow:
		emit_flush();
		call_vv(evm_pow);
		break;
	    case emit_code_hypot:
		emit_flush();
		call_vv(evm_hypot);
		break;
	    case emit_code_complex:
		emit_flush();
		call_vv(evm_complex);
		break;
	    case emit_code_incdec:
		emit_load_incdec(null, emit.ast0);
		break;
	    case emit_code_incdec_store:
		emit_load_incdec_store(null, emit.ast0, emit.ast1);
		break;
	    case emit_code_boolean:
		emit_boolean(emit.ast0);
		break;
	    case emit_code_com:
		emit_flush();
		call_vv(evm_com);
		break;
	    case emit_code_inv:
		emit_flush();
		call_vv(evm_inv);
		break;
	    case emit_code_neg:
		emit_flush();
		call_vv(evm_neg);
		break;
	    case emit_code_signbit:
		emit_flush();
		call_vv(evm_signbit);
		break;
	    case emit_code_signum:
		emit_flush();
		call_vv(evm_signum);
		break;
	    case emit_code_rational:
		emit_flush();
		call_vv(evm_rational);
		break;
	    case emit_code_integer_p:
		emit_flush();
		call_vv(evm_integer_p);
		break;
	    case emit_code_rational_p:
		emit_flush();
		call_vv(evm_rational_p);
		break;
	    case emit_code_float_p:
		emit_flush();
		call_vv(evm_float_p);
		break;
	    case emit_code_real_p:
		emit_flush();
		call_vv(evm_real_p);
		break;
	    case emit_code_complex_p:
		emit_flush();
		call_vv(evm_complex_p);
		break;
	    case emit_code_number_p:
		emit_flush();
		call_vv(evm_number_p);
		break;
	    case emit_code_finite_p:
		emit_flush();
		call_vv(evm_finite_p);
		break;
	    case emit_code_inf_p:
		emit_flush();
		call_vv(evm_inf_p);
		break;
	    case emit_code_nan_p:
		emit_flush();
		call_vv(evm_nan_p);
		break;
	    case emit_code_num:
		emit_flush();
		call_vv(evm_num);
		break;
	    case emit_code_den:
		emit_flush();
		call_vv(evm_den);
		break;
	    case emit_code_real:
		emit_flush();
		call_vv(evm_real);
		break;
	    case emit_code_imag:
		emit_flush();
		call_vv(evm_imag);
		break;
	    case emit_code_arg:
		emit_flush();
		call_vv(evm_arg);
		break;
	    case emit_code_conj:
		emit_flush();
		call_vv(evm_conj);
		break;
	    case emit_code_floor:
		emit_flush();
		call_vv(evm_floor);
		break;
	    case emit_code_trunc:
		emit_flush();
		call_vv(evm_trunc);
		break;
	    case emit_code_round:
		emit_flush();
		call_vv(evm_round);
		break;
	    case emit_code_ceil:
		emit_flush();
		call_vv(evm_ceil);
		break;
	    case emit_code_abs:
		emit_flush();
		call_vv(evm_abs);
		break;
	    case emit_code_sqrt:
		emit_flush();
		call_vv(evm_sqrt);
		break;
	    case emit_code_cbrt:
		emit_flush();
		call_vv(evm_cbrt);
		break;
	    case emit_code_sin:
		emit_flush();
		call_vv(evm_sin);
		break;
	    case emit_code_cos:
		emit_flush();
		call_vv(evm_cos);
		break;
	    case emit_code_tan:
		emit_flush();
		call_vv(evm_tan);
		break;
	    case emit_code_asin:
		emit_flush();
		call_vv(evm_asin);
		break;
	    case emit_code_acos:
		emit_flush();
		call_vv(evm_acos);
		break;
	    case emit_code_atan:
		emit_flush();
		call_vv(evm_atan);
		break;
	    case emit_code_sinh:
		emit_flush();
		call_vv(evm_sinh);
		break;
	    case emit_code_cosh:
		emit_flush();
		call_vv(evm_cosh);
		break;
	    case emit_code_tanh:
		emit_flush();
		call_vv(evm_tanh);
		break;
	    case emit_code_asinh:
		emit_flush();
		call_vv(evm_asinh);
		break;
	    case emit_code_acosh:
		emit_flush();
		call_vv(evm_acosh);
		break;
	    case emit_code_atanh:
		emit_flush();
		call_vv(evm_atanh);
		break;
	    case emit_code_proj:
		emit_flush();
		call_vv(evm_proj);
		break;
	    case emit_code_exp:
		emit_flush();
		call_vv(evm_exp);
		break;
	    case emit_code_log:
		emit_flush();
		call_vv(evm_log);
		break;
	    case emit_code_log2:
		emit_flush();
		call_vv(evm_log2);
		break;
	    case emit_code_log10:
		emit_flush();
		call_vv(evm_log10);
		break;
	    case emit_code_new:
		emit_clear();
		call_vii(evm_new, east_node(emit.ast0)->crecord.record->type,
			 east_node(emit.ast0)->crecord.record->size);
		break;
	    case emit_code_mv:
		emit_flush();
		call_vi(evm_mv, east_node(emit.ast0)->crecord.record->size);
		break;
	    case emit_code_vnew:
		emit_flush();
		call_vii(evm_vnew, east_node(emit.ast0)->cvector.type,
			 east_node(emit.ast0)->cvector.rank);
		break;
	    case emit_code_vnewi:
		emit_flush();
		emit_vnewi(east_node(emit.ast0));
		break;
	    case emit_code_vmv:
		emit_flush();
		call_vi(evm_vmv, east_node(emit.ast0)->cvector.length);
		break;
	    case emit_code_renew:
		emit_flush();
		call_vv(evm_renew);
		break;
	    case emit_code_renewi:
		emit_flush();
		call_vi(evm_renewi, east_node(emit.ast0)->cvector.length);
		break;
	    case emit_code_fref:
		emit_clear();
		call_viii(evm_fref, east_node(emit.ast0)->capply.offset,
			  east_node(emit.ast0)->capply.length,
			  east_node(emit.ast0)->capply.arg);
		break;
	    case emit_code_apply:
		emit_flush();
		/* FIXME only require jumping into the function code
		 * if not a builtin (otherwise, the returned address
		 * is just the address after evm_apply call...) */
		jit_prepare(jit_call_default);
		jit_pushargi(east_node(emit.ast0)->capply.length);
		jinstr = jit_movi(JIT_R0, 0);
		jit_pushargr(JIT_R0);
		jit_finishi(evm_apply);
		jit_retval(JIT_R0);
		jit_jmpr(JIT_R0);
		/* where ret* will arrive */
		jit_patch(jinstr);
		break;
	    case emit_code_aret:
		emit_sync();
		call_vv(evm_aret);
		/* implicit type not modified */
		break;
	    case emit_code_szof:
		emit_flush();
		call_vv(evm_szof);
		break;
	    case emit_code_szofva:
		emit_flush();
		call_vv(evm_szofva);
		break;
	    case emit_code_szofdim:
		emit_flush();
		call_vv(evm_szofdim);
		break;
	    case emit_code_szofdimi:
		emit_flush();
		call_vi(evm_szofdimi, east_node(emit.ast0)->cvector.arg);
		break;
	    case emit_code_tpof:
		emit_flush();
		call_vv(evm_tpof);
		break;
	    case emit_code_subtpof:
		emit_flush();
		call_vv(evm_subtpof);
		break;
	    case emit_code_rankof:
		emit_flush();
		call_vv(evm_rankof);
		break;
	    case emit_code_vas:
		emit_sync();
		call_vi(evm_vas, east_node(emit.ast0)->cstack.length);
		/* implicit type not modified */
		break;
	    case emit_code_val:
		emit_sync();
		call_vi(evm_val, east_node(emit.ast0)->cstack.length);
		/* implicit type not modified */
		break;
	    case emit_code_lva:
		emit_flush();
		call_vi(evm_lva, east_node(emit.ast0)->cstack.length);
		break;
	    case emit_code_lvai:
		emit_flush();
		call_vii(evm_lvai, east_node(emit.ast0)->cstack.length,
			 east_node(emit.ast0)->cstack.offset);
		break;
	    case emit_code_sva:
		emit_sync();
		call_vi(evm_sva, east_node(emit.ast0)->cstack.length);
		/* implicit type not modified */
		break;
	    case emit_code_svai:
		emit_sync();
		call_vii(evm_svai, east_node(emit.ast0)->cstack.length,
			 east_node(emit.ast0)->cstack.offset);
		/* implicit type not modified */
		break;
	    case emit_code_pva:
		emit_flush();
		call_vi(evm_pva, east_node(emit.ast0)->cstack.length);
		break;
	    case emit_code_try:
		emit_flush();
		emit_try(emit.list);
		emit.list = east_next(emit.list);
		break;
	    case emit_code_catch:
		emit_flush();
		call_vv(evm_catch);
		break;
	    case emit_code_throw:
		emit_flush();
		jit_prepare(jit_call_default);
		jit_pushargi(east_node(emit.ast0)->cexcept.arg);
		jit_finishi(evm_throw);
		/* jump to catch handler */
		jit_retval(JIT_R0);
		jit_jmpr(JIT_R0);
		break;
	    case emit_code_unwind:
		emit_sync();
		call_vv(evm_unwind);
		/* implicit type not modified */
		break;
	    case emit_code_exit:
		emit_clear();
		if (function->name == null) {
		    thread_exit_label = jit_label();
		    thread_exit_label->flag |= jit_flag_use;
		}
		/* if not main thread, this call never returns */
		call_vv(evm_exit);
		/* if main thread, return from jit */
		jit_reti(0);
		break;
	    case emit_code_setup:
		emit_sync();
		call_vii(evm_setup,
			 east_node(emit.ast0)->csymbol.symbol->offset,
			 east_node(emit.ast0)->csymbol.symbol->type);
		break;
	    case emit_code_intp_0:
		emit_flush();
		call_vv(evm_intp_0);
		break;
	    case emit_code_intp_v:
		emit_flush();
		call_vv(evm_intp_v);
		break;
	    case emit_code_intp_n1:
		emit_flush();
		call_vv(evm_intp_n1);
		break;
	    case emit_code_realp_v:
		emit_flush();
		call_vv(evm_realp_v);
		break;
	    case emit_code_nump_0:
		emit_flush();
		call_vv(evm_nump_0);
		break;
	    case emit_code_nump_v:
		emit_flush();
		call_vv(evm_nump_v);
		break;
	    case emit_code_block:
		/* implicit type not modified as blocks are a special
		 * kind of label, that are known to not be the target
		 * of any jump, as they are actually code following
		 * conditional branches */
		break;
	    default:
		abort();
	}
	if (emit.note)
	    emit.note->cnote.note = jit_note(emit.note);
    }

    /* patch forward jumps */
    for (offset = 0; offset < patch_vector->offset; offset += 2)
	jit_patch_at((jit_node_t *)patches[offset + 1],
		     (jit_node_t *)labels[patches[offset]]);

    /* setup switch information to patch jumps once jit is finished */
    for (offset = table_offset; offset < table_vector->offset; offset++)
	table_forward(els[tables[offset]]);

    jinstr = jit_label();
    jinstr->flag |= jit_flag_use;
    function->epilog = jinstr;
#endif
}
